@cognigy/webchat 3.52.0 → 3.53.0

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.
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * webchat.esm.js v3.52.0
3
- * https://github.com/Cognigy/Webchat/tree/v3.52.0
4
- * https://github.com/Cognigy/Webchat/tree/v3.52.0/OSS_LICENSES.txt
2
+ * webchat.esm.js v3.53.0
3
+ * https://github.com/Cognigy/Webchat/tree/v3.53.0
4
+ * https://github.com/Cognigy/Webchat/tree/v3.53.0/OSS_LICENSES.txt
5
5
  */
6
6
  import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react";
7
7
  import * as __WEBPACK_EXTERNAL_MODULE_react_dom_7dac9eee__ from "react-dom";
@@ -589,6 +589,89 @@ var SvgMinimize16Px = function SvgMinimize16Px(_ref) {
589
589
 
590
590
  /***/ },
591
591
 
592
+ /***/ 21909
593
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
594
+
595
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
596
+ /* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__)
597
+ /* harmony export */ });
598
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
599
+ var _rect, _rect2;
600
+ var _excluded = ["title", "titleId"];
601
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
602
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
603
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
604
+
605
+ var SvgPause16Px = function SvgPause16Px(_ref) {
606
+ var title = _ref.title,
607
+ titleId = _ref.titleId,
608
+ props = _objectWithoutProperties(_ref, _excluded);
609
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("svg", _extends({
610
+ xmlns: "http://www.w3.org/2000/svg",
611
+ width: 16,
612
+ height: 16,
613
+ fill: "none",
614
+ "aria-hidden": "true",
615
+ viewBox: "0 0 16 16",
616
+ "aria-labelledby": titleId
617
+ }, props), title ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("title", {
618
+ id: titleId
619
+ }, title) : null, _rect || (_rect = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("rect", {
620
+ width: 4,
621
+ height: 12,
622
+ x: 3,
623
+ y: 2,
624
+ fill: "currentColor",
625
+ rx: 1
626
+ })), _rect2 || (_rect2 = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("rect", {
627
+ width: 4,
628
+ height: 12,
629
+ x: 9,
630
+ y: 2,
631
+ fill: "currentColor",
632
+ rx: 1
633
+ })));
634
+ };
635
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SvgPause16Px);
636
+
637
+ /***/ },
638
+
639
+ /***/ 6663
640
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
641
+
642
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
643
+ /* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__)
644
+ /* harmony export */ });
645
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
646
+ var _path;
647
+ var _excluded = ["title", "titleId"];
648
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
649
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
650
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
651
+
652
+ var SvgPlay16Px = function SvgPlay16Px(_ref) {
653
+ var title = _ref.title,
654
+ titleId = _ref.titleId,
655
+ props = _objectWithoutProperties(_ref, _excluded);
656
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("svg", _extends({
657
+ xmlns: "http://www.w3.org/2000/svg",
658
+ width: 16,
659
+ height: 16,
660
+ fill: "none",
661
+ "aria-hidden": "true",
662
+ viewBox: "0 0 16 16",
663
+ "aria-labelledby": titleId
664
+ }, props), title ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("title", {
665
+ id: titleId
666
+ }, title) : null, _path || (_path = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("path", {
667
+ fill: "currentColor",
668
+ d: "M5.5 2.6a1 1 0 0 0-1.5.86v9.08a1 1 0 0 0 1.5.86l7.6-4.54a1 1 0 0 0 0-1.72z"
669
+ })));
670
+ };
671
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SvgPlay16Px);
672
+
673
+ /***/ },
674
+
592
675
  /***/ 34967
593
676
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
594
677
 
@@ -35116,6 +35199,33 @@ if (!g.fetch) {
35116
35199
 
35117
35200
  /***/ },
35118
35201
 
35202
+ /***/ 36886
35203
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
35204
+
35205
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
35206
+ /* harmony export */ Z: () => (/* binding */ ALWAYS_BLOCKED_TAGS)
35207
+ /* harmony export */ });
35208
+ // Hard deny-list for HTML tags that must never be permitted via customAllowedHtmlTags,
35209
+ // regardless of tenant configuration. Each tag enables a distinct attack vector:
35210
+ // applet — Java applet execution
35211
+ // base — rewrites all relative URLs on the host page
35212
+ // body / html / head — structural document elements; no legitimate use in sanitised fragments
35213
+ // embed — loads arbitrary external content / plugins
35214
+ // form — posts user data to attacker-controlled URLs
35215
+ // frame / frameset / noframes — clickjacking and legacy frame injection
35216
+ // iframe — inline HTML documents; srcdoc = direct XSS vector
35217
+ // link — loads external stylesheets
35218
+ // meta — HTTP redirects and CSP bypass via http-equiv
35219
+ // object — loads Flash, PDFs, and arbitrary external content
35220
+ // style — CSS injection and attribute-value exfiltration
35221
+ //
35222
+ // This file has no imports so it can be consumed by both sanitize.ts (which imports
35223
+ // the Redux store) and config-reducer.ts (which the store imports) without creating
35224
+ // a circular dependency between them.
35225
+ const ALWAYS_BLOCKED_TAGS = new Set(["script", "iframe", "object", "embed", "applet", "frame", "frameset", "noframes", "meta", "base", "link", "style", "form", "body", "html", "head"]);
35226
+
35227
+ /***/ },
35228
+
35119
35229
  /***/ 30871
35120
35230
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
35121
35231
 
@@ -35765,35 +35875,38 @@ const Modal = ({
35765
35875
  /* harmony import */ var _webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(44557);
35766
35876
  /* harmony import */ var _webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(31067);
35767
35877
  /* harmony import */ var _presentational_FABDisabled__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(99526);
35768
- /* harmony import */ var _webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(69401);
35769
- /* harmony import */ var _presentational_HomeScreen__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(42740);
35770
- /* harmony import */ var _presentational_previous_conversations_ConversationsList__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(14704);
35771
- /* harmony import */ var _cognigy_chat_components__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(2854);
35772
- /* harmony import */ var _presentational_previous_conversations_helpers__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(21013);
35773
- /* harmony import */ var _presentational_InformationMessage__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(25850);
35774
- /* harmony import */ var _presentational_PrivacyNotice__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(9981);
35775
- /* harmony import */ var _presentational_chat_options_ChatOptions__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(49687);
35776
- /* harmony import */ var _history_QueueUpdates__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(59021);
35777
- /* harmony import */ var _plugins_input_file_DropZone__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(1339);
35778
- /* harmony import */ var react_transition_group__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(66492);
35779
- /* harmony import */ var _presentational_TeaserMessage__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(49888);
35780
- /* harmony import */ var _presentational_TeaserMessageAnnouncer__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(59359);
35781
- /* harmony import */ var _presentational_UnreadMessagesAnnouncer__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(1715);
35782
- /* harmony import */ var _functional_xapp_overlay_XAppOverlay__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(12381);
35783
- /* harmony import */ var _utils_sourceMapping__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(25091);
35784
- /* harmony import */ var _plugins_speech_output__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(83679);
35785
- /* harmony import */ var _utils_filter_out_control_commands__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(70562);
35786
- /* harmony import */ var _utils_isMobile__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(40794);
35787
- /* harmony import */ var _webchat_helper_handleMarkdown__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(71621);
35788
- /* harmony import */ var _presentational_previous_conversations_DeleteAllConversations__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(5196);
35789
- /* harmony import */ var _presentational_ScreenReaderLiveRegion__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(41426);
35790
- /* harmony import */ var _presentational_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(63813);
35791
- /* harmony import */ var _presentational_FreezeOnExit__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(97475);
35792
- /* harmony import */ var _presentational_HomeScreenAnnouncer__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(55115);
35793
- /* harmony import */ var _utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(39191);
35794
- /* harmony import */ var _utils_screen_visibility__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(48245);
35795
- /* harmony import */ var classnames__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(14176);
35796
- /* harmony import */ var classnames__WEBPACK_IMPORTED_MODULE_58___default = /*#__PURE__*/__webpack_require__.n(classnames__WEBPACK_IMPORTED_MODULE_58__);
35878
+ /* harmony import */ var _presentational_IconAnimationPauseButton__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(71374);
35879
+ /* harmony import */ var _webchat_helper_storage__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(62473);
35880
+ /* harmony import */ var _webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(69401);
35881
+ /* harmony import */ var _presentational_HomeScreen__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(42740);
35882
+ /* harmony import */ var _presentational_previous_conversations_ConversationsList__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(14704);
35883
+ /* harmony import */ var _cognigy_chat_components__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(2854);
35884
+ /* harmony import */ var _presentational_previous_conversations_helpers__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(21013);
35885
+ /* harmony import */ var _presentational_InformationMessage__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(25850);
35886
+ /* harmony import */ var _presentational_PrivacyNotice__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(9981);
35887
+ /* harmony import */ var _presentational_chat_options_ChatOptions__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(49687);
35888
+ /* harmony import */ var _history_QueueUpdates__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(59021);
35889
+ /* harmony import */ var _plugins_input_file_DropZone__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(1339);
35890
+ /* harmony import */ var react_transition_group__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(66492);
35891
+ /* harmony import */ var _presentational_TeaserMessage__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(49888);
35892
+ /* harmony import */ var _presentational_TeaserMessageAnnouncer__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(59359);
35893
+ /* harmony import */ var _presentational_UnreadMessagesAnnouncer__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(1715);
35894
+ /* harmony import */ var _functional_xapp_overlay_XAppOverlay__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(12381);
35895
+ /* harmony import */ var _utils_sourceMapping__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(25091);
35896
+ /* harmony import */ var _plugins_speech_output__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(83679);
35897
+ /* harmony import */ var _utils_filter_out_control_commands__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(70562);
35898
+ /* harmony import */ var _utils_isMobile__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(40794);
35899
+ /* harmony import */ var _webchat_helper_handleMarkdown__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(71621);
35900
+ /* harmony import */ var _presentational_previous_conversations_DeleteAllConversations__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(5196);
35901
+ /* harmony import */ var _presentational_ScreenReaderLiveRegion__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(41426);
35902
+ /* harmony import */ var _presentational_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(63813);
35903
+ /* harmony import */ var _presentational_FreezeOnExit__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(97475);
35904
+ /* harmony import */ var _presentational_HomeScreenAnnouncer__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(55115);
35905
+ /* harmony import */ var _presentational_FileUploadAnnouncer__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(32735);
35906
+ /* harmony import */ var _utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(39191);
35907
+ /* harmony import */ var _utils_screen_visibility__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(48245);
35908
+ /* harmony import */ var classnames__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(14176);
35909
+ /* harmony import */ var classnames__WEBPACK_IMPORTED_MODULE_61___default = /*#__PURE__*/__webpack_require__.n(classnames__WEBPACK_IMPORTED_MODULE_61__);
35797
35910
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
35798
35911
 
35799
35912
 
@@ -35850,11 +35963,18 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
35850
35963
 
35851
35964
 
35852
35965
 
35966
+
35967
+
35968
+
35853
35969
 
35854
35970
 
35855
35971
 
35856
35972
 
35857
35973
  const stylisPlugins = [(0,_utils_css__WEBPACK_IMPORTED_MODULE_8__/* .isolate */ .az)("[data-cognigy-webchat-root]")];
35974
+ const ICON_ANIMATION_PAUSED_STORAGE_KEY = "cognigy-webchat-icon-animation-paused";
35975
+
35976
+ /** `layout.iconAnimation` names an animation ("bounce" | "pulse" | "swing"). */
35977
+ const isIconAnimationConfigured = animation => typeof animation === "string" && animation.trim().length > 0 && animation !== "none";
35858
35978
 
35859
35979
  /**
35860
35980
  * for RTL-layout websites, use the stylis-rtl plugin to convert CSS,
@@ -35870,7 +35990,7 @@ const styleCache = (0,_emotion_cache__WEBPACK_IMPORTED_MODULE_7__/* ["default"]
35870
35990
  key: "cognigy-webchat",
35871
35991
  stylisPlugins
35872
35992
  });
35873
- const TopStatusMessage = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .A)(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_34__/* .Typography */ .o5)(({
35993
+ const TopStatusMessage = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .A)(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_36__/* .Typography */ .o5)(({
35874
35994
  theme
35875
35995
  }) => ({
35876
35996
  display: "block",
@@ -35954,9 +36074,10 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
35954
36074
  timedOut: false,
35955
36075
  showDeleteAllConversationsModal: false,
35956
36076
  deleteConversationsModalState: false,
36077
+ isIconAnimationPaused: false,
35957
36078
  liveContent: {},
35958
36079
  isMobile: false,
35959
- noticeSession: _utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_56__/* .INITIAL_NOTICE_SESSION */ .j_
36080
+ noticeSession: _utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_59__/* .INITIAL_NOTICE_SESSION */ .j_
35960
36081
  };
35961
36082
  unreadTitleIndicatorInterval = null;
35962
36083
  originalTitle = window.document.title;
@@ -35966,6 +36087,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
35966
36087
  visibilityCheckCompleted = false;
35967
36088
  engagementMessageTimeout = null;
35968
36089
  ratingFocusTimeout = null;
36090
+ deleteAllFocusTimeout = null;
35969
36091
  homeScreenExitFocusTimeout = null;
35970
36092
  xAppOverlayCloseFocusTimeout = null;
35971
36093
 
@@ -35993,6 +36115,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
35993
36115
  this.closeButtonInHeaderRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
35994
36116
  this.menuButtonInHeaderRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
35995
36117
  this.deleteButtonInHeaderRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
36118
+ this.emptyPrevConversationsTextRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
35996
36119
  this.startNewConversationButtonRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
35997
36120
  this.ratingButtonInHeaderRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
35998
36121
  this.webchatWindowRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
@@ -36005,9 +36128,9 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36005
36128
  const secondaryColor = props?.config?.settings?.colors?.secondaryColor;
36006
36129
  const chatInterfaceColor = props?.config?.settings?.colors?.chatInterfaceColor;
36007
36130
  const sourceColorMapping = props?.config?.settings?.widgetSettings?.sourceColorMapping;
36008
- const overrideBotMessageColor = (0,_utils_sourceMapping__WEBPACK_IMPORTED_MODULE_46__/* .getSourceBackgroundColor */ .N)(sourceColorMapping?.bot, props?.config?.settings?.colors);
36009
- const overrideUserMessageColor = (0,_utils_sourceMapping__WEBPACK_IMPORTED_MODULE_46__/* .getSourceBackgroundColor */ .N)(sourceColorMapping?.user, props?.config?.settings?.colors);
36010
- const overrideAgentMessageColor = (0,_utils_sourceMapping__WEBPACK_IMPORTED_MODULE_46__/* .getSourceBackgroundColor */ .N)(sourceColorMapping?.agent, props?.config?.settings?.colors);
36131
+ const overrideBotMessageColor = (0,_utils_sourceMapping__WEBPACK_IMPORTED_MODULE_48__/* .getSourceBackgroundColor */ .N)(sourceColorMapping?.bot, props?.config?.settings?.colors);
36132
+ const overrideUserMessageColor = (0,_utils_sourceMapping__WEBPACK_IMPORTED_MODULE_48__/* .getSourceBackgroundColor */ .N)(sourceColorMapping?.user, props?.config?.settings?.colors);
36133
+ const overrideAgentMessageColor = (0,_utils_sourceMapping__WEBPACK_IMPORTED_MODULE_48__/* .getSourceBackgroundColor */ .N)(sourceColorMapping?.agent, props?.config?.settings?.colors);
36011
36134
  const overrideBotMessageBorderColor = sourceColorMapping?.bot === "user" ? "transparent" : null;
36012
36135
  const overrideUserMessageBorderColor = sourceColorMapping?.user === "bot" ? state.theme.black80 : null;
36013
36136
  const overrideAgentMessageBorderColor = sourceColorMapping?.agent === "user" ? "transparent" : null;
@@ -36101,7 +36224,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36101
36224
  }
36102
36225
  }
36103
36226
  }
36104
- this.hideNotifications = (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isDisabledDueToConnectivity */ .l9)(this.props.config.settings, timeoutReached) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isDisabledDueToMaintenance */ .$6)(this.props.config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isDisabledOutOfBusinessHours */ .dt)(this.props.config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isInformingDueToConnectivity */ .u0)(this.props.config.settings, timeoutReached) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isInformingDueToMaintenance */ .iF)(this.props.config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isInformingOutOfBusinessHours */ .Gr)(this.props.config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isHiddenDueToConnectivity */ .VD)(this.props.config.settings, timeoutReached) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isHiddenDueToMaintenance */ .Hs)(this.props.config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isHiddenOutOfBusinessHours */ .hN)(this.props.config.settings.businessHours) || false;
36227
+ this.hideNotifications = (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isDisabledDueToConnectivity */ .l9)(this.props.config.settings, timeoutReached) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isDisabledDueToMaintenance */ .$6)(this.props.config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isDisabledOutOfBusinessHours */ .dt)(this.props.config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isInformingDueToConnectivity */ .u0)(this.props.config.settings, timeoutReached) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isInformingDueToMaintenance */ .iF)(this.props.config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isInformingOutOfBusinessHours */ .Gr)(this.props.config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isHiddenDueToConnectivity */ .VD)(this.props.config.settings, timeoutReached) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isHiddenDueToMaintenance */ .Hs)(this.props.config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isHiddenOutOfBusinessHours */ .hN)(this.props.config.settings.businessHours) || false;
36105
36228
  };
36106
36229
 
36107
36230
  // `prevConversationsSnapshot` must predate the session change — see
@@ -36111,14 +36234,14 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36111
36234
  evaluateNoticeSession(prevConversationsSnapshot) {
36112
36235
  const id = this.props.currentSession || "";
36113
36236
  this.setState(prev => ({
36114
- noticeSession: (0,_utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_56__/* .computeNoticeSession */ .Kr)(prev.noticeSession, id, prevConversationsSnapshot, this.props.hasRestoredPersistedHistory, this.props.willRestorePersistedConversation)
36237
+ noticeSession: (0,_utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_59__/* .computeNoticeSession */ .Kr)(prev.noticeSession, id, prevConversationsSnapshot, this.props.hasRestoredPersistedHistory, this.props.willRestorePersistedConversation)
36115
36238
  }));
36116
36239
  }
36117
36240
  handleNoticeIntroAnnounced = introKey => {
36118
36241
  this.announcedNoticeKeys.add(introKey);
36119
36242
  };
36120
36243
  getNoticeIntro() {
36121
- const text = (0,_utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_56__/* .getAIAgentNoticeIntroText */ .bu)({
36244
+ const text = (0,_utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_59__/* .getAIAgentNoticeIntroText */ .bu)({
36122
36245
  behavior: this.props.config.settings.behavior,
36123
36246
  showDisconnectOverlay: this.showDisconnectOverlay,
36124
36247
  noticeSession: this.state.noticeSession,
@@ -36134,14 +36257,17 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36134
36257
  componentDidMount() {
36135
36258
  const defaultMessagePlugins = [];
36136
36259
  if (this.props.ttsActive) {
36137
- defaultMessagePlugins.push(_plugins_speech_output__WEBPACK_IMPORTED_MODULE_47__/* ["default"] */ .A);
36260
+ defaultMessagePlugins.push(_plugins_speech_output__WEBPACK_IMPORTED_MODULE_49__/* ["default"] */ .A);
36138
36261
  }
36139
36262
  this.setState({
36140
36263
  inputPlugins: [...(this.props.inputPlugins || []), _plugins_input_base__WEBPACK_IMPORTED_MODULE_11__/* ["default"] */ .A],
36141
36264
  messagePlugins: [...(this.props.messagePlugins || []), ...defaultMessagePlugins],
36142
- isMobile: (0,_utils_isMobile__WEBPACK_IMPORTED_MODULE_49__/* .isMobileViewport */ .$)()
36265
+ isMobile: (0,_utils_isMobile__WEBPACK_IMPORTED_MODULE_51__/* .isMobileViewport */ .$)(),
36266
+ isIconAnimationPaused: this.readIconAnimationPaused()
36143
36267
  });
36144
- this.setupIconAnimationInterval();
36268
+ // After the state above is committed: the interval is only started
36269
+ // when the animation is not paused.
36270
+ this.setState({}, () => this.setupIconAnimationInterval());
36145
36271
 
36146
36272
  // No pre-change snapshot exists on mount; the current map predates
36147
36273
  // any message activity of this page load.
@@ -36257,7 +36383,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36257
36383
  if (prevProps.ttsActive !== this.props.ttsActive || prevProps.inputPlugins !== this.props.inputPlugins || prevProps.messagePlugins !== this.props.messagePlugins) {
36258
36384
  const defaultMessagePlugins = [];
36259
36385
  if (this.props.ttsActive) {
36260
- defaultMessagePlugins.push(_plugins_speech_output__WEBPACK_IMPORTED_MODULE_47__/* ["default"] */ .A);
36386
+ defaultMessagePlugins.push(_plugins_speech_output__WEBPACK_IMPORTED_MODULE_49__/* ["default"] */ .A);
36261
36387
  }
36262
36388
  this.setState({
36263
36389
  inputPlugins: [...(this.props.inputPlugins || []), _plugins_input_base__WEBPACK_IMPORTED_MODULE_11__/* ["default"] */ .A],
@@ -36307,7 +36433,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36307
36433
  // TODO: Add markdown rendering if there is valid markdown and renderMarkdown is enabled
36308
36434
  // adding react-markdown is blocked by updating React to v18
36309
36435
  if (!this.props.config.settings.widgetSettings.disableTeaserMarkdownRemoval) {
36310
- lastUnseenMessageText = (0,_webchat_helper_handleMarkdown__WEBPACK_IMPORTED_MODULE_50__/* .removeMarkdownChars */ .$)(lastUnseenMessageText);
36436
+ lastUnseenMessageText = (0,_webchat_helper_handleMarkdown__WEBPACK_IMPORTED_MODULE_52__/* .removeMarkdownChars */ .$)(lastUnseenMessageText);
36311
36437
  }
36312
36438
  this.setState({
36313
36439
  lastUnseenMessageText
@@ -36326,7 +36452,10 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36326
36452
  }
36327
36453
 
36328
36454
  // Re-evaluate icon animation interval when related settings change
36329
- if (prevProps?.config?.settings?.layout?.iconAnimationInterval !== this.props?.config?.settings?.layout?.iconAnimationInterval || prevProps?.config?.settings?.layout?.iconAnimationSpeed !== this.props?.config?.settings?.layout?.iconAnimationSpeed || prevProps?.config?.settings?.layout?.iconAnimation !== this.props?.config?.settings?.layout?.iconAnimation) {
36455
+ if (prevProps?.config?.settings?.layout?.iconAnimationInterval !== this.props?.config?.settings?.layout?.iconAnimationInterval || prevProps?.config?.settings?.layout?.iconAnimationSpeed !== this.props?.config?.settings?.layout?.iconAnimationSpeed || prevProps?.config?.settings?.layout?.iconAnimation !== this.props?.config?.settings?.layout?.iconAnimation ||
36456
+ // A persisted pause only counts while the control is offered, so
36457
+ // toggling the opt-in must restart (or stop) the timer as well
36458
+ prevProps?.config?.settings?.layout?.enableIconAnimationPauseButton !== this.props?.config?.settings?.layout?.enableIconAnimationPauseButton) {
36330
36459
  this.setupIconAnimationInterval();
36331
36460
  }
36332
36461
  if (!this.hideNotifications) {
@@ -36364,6 +36493,10 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36364
36493
  clearTimeout(this.ratingFocusTimeout);
36365
36494
  this.ratingFocusTimeout = null;
36366
36495
  }
36496
+ if (this.deleteAllFocusTimeout) {
36497
+ clearTimeout(this.deleteAllFocusTimeout);
36498
+ this.deleteAllFocusTimeout = null;
36499
+ }
36367
36500
 
36368
36501
  // also removes the fallback's document pointerdown listener
36369
36502
  this.cancelHomeScreenExitFocusFallback();
@@ -36380,7 +36513,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36380
36513
  window.removeEventListener("resize", this.handleResize);
36381
36514
  }
36382
36515
  handleResize() {
36383
- const isMobile = (0,_utils_isMobile__WEBPACK_IMPORTED_MODULE_49__/* .isMobileViewport */ .$)();
36516
+ const isMobile = (0,_utils_isMobile__WEBPACK_IMPORTED_MODULE_51__/* .isMobileViewport */ .$)();
36384
36517
  if (isMobile !== this.state.isMobile) {
36385
36518
  this.setState({
36386
36519
  isMobile
@@ -36393,9 +36526,15 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36393
36526
  clearInterval(this.iconAnimationIntervalHandle);
36394
36527
  this.iconAnimationIntervalHandle = null;
36395
36528
  }
36396
- const animation = this.props?.config?.settings?.layout?.iconAnimation;
36397
- // If there is no animation configured, do not start the timer
36398
- if (!animation || typeof animation === "string" && animation.trim().length === 0) {
36529
+ // No timer without an animation
36530
+ if (!isIconAnimationConfigured(this.props?.config?.settings?.layout?.iconAnimation)) {
36531
+ return;
36532
+ }
36533
+ // … nor while the user has paused it (handleToggleIconAnimationPause and
36534
+ // componentDidUpdate re-run this on every change). A burst that is
36535
+ // playing right now is cut short instead of finishing.
36536
+ if (this.isIconAnimationPaused()) {
36537
+ this.chatToggleButtonRef?.current?.querySelector(".iconAnimationContainer")?.classList.remove("optionActive");
36399
36538
  return;
36400
36539
  }
36401
36540
  const intervalSec = this.props.config?.settings?.layout?.iconAnimationInterval ?? 5;
@@ -36413,6 +36552,47 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36413
36552
  }, intervalMs);
36414
36553
  }
36415
36554
 
36555
+ /**
36556
+ * Pause / resume of the launcher icon animation (WCAG 2.2.2 — CGY-39786).
36557
+ * The choice is kept in the configured browser storage so it survives a
36558
+ * reload; with `disableLocalStorage` it lasts for the page lifetime only.
36559
+ */
36560
+ readIconAnimationPaused() {
36561
+ try {
36562
+ const storage = (0,_webchat_helper_storage__WEBPACK_IMPORTED_MODULE_32__/* .getStorage */ .c)(this.props.config.settings.embeddingConfiguration);
36563
+ return storage?.getItem(ICON_ANIMATION_PAUSED_STORAGE_KEY) === "true";
36564
+ } catch {
36565
+ return false;
36566
+ }
36567
+ }
36568
+ persistIconAnimationPaused(paused) {
36569
+ try {
36570
+ const storage = (0,_webchat_helper_storage__WEBPACK_IMPORTED_MODULE_32__/* .getStorage */ .c)(this.props.config.settings.embeddingConfiguration);
36571
+ storage?.setItem(ICON_ANIMATION_PAUSED_STORAGE_KEY, String(paused));
36572
+ } catch {
36573
+ // Storage may be unavailable (restricted environments) — the state
36574
+ // still applies for this page load.
36575
+ }
36576
+ }
36577
+
36578
+ /**
36579
+ * A stored pause is only honored while the pause button is offered
36580
+ * (`layout.enableIconAnimationPauseButton`). Otherwise a value persisted
36581
+ * earlier, or a later `updateSettings()` that switches the opt-in off,
36582
+ * would leave the animation stopped with no control to resume it.
36583
+ */
36584
+ isIconAnimationPaused() {
36585
+ return !!this.props.config?.settings?.layout?.enableIconAnimationPauseButton && this.state.isIconAnimationPaused;
36586
+ }
36587
+ handleToggleIconAnimationPause = () => {
36588
+ const paused = !this.state.isIconAnimationPaused;
36589
+ // Stops (or restarts) the interval itself once the state is committed
36590
+ this.setState({
36591
+ isIconAnimationPaused: paused
36592
+ }, () => this.setupIconAnimationInterval());
36593
+ this.persistIconAnimationPaused(paused);
36594
+ };
36595
+
36416
36596
  /**
36417
36597
  * This triggers the engagement message in case the webchat
36418
36598
  * was not yet open and the history is empty
@@ -36467,7 +36647,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36467
36647
  const {
36468
36648
  messages
36469
36649
  } = this.props;
36470
- if ((0,_presentational_previous_conversations_helpers__WEBPACK_IMPORTED_MODULE_35__/* .isConversationEnded */ .GP)(messages)) return null;
36650
+ if ((0,_presentational_previous_conversations_helpers__WEBPACK_IMPORTED_MODULE_37__/* .isConversationEnded */ .GP)(messages)) return null;
36471
36651
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_plugins_InputPluginRenderer__WEBPACK_IMPORTED_MODULE_10__/* ["default"] */ .A, {
36472
36652
  plugins: inputPlugins,
36473
36653
  messages: this.props.messages,
@@ -36784,16 +36964,21 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36784
36964
  disableMobileScrollLock
36785
36965
  }
36786
36966
  } = config.settings;
36787
- if (!config.active && !config.settings.embeddingConfiguration.connectivity.enabled || !config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig || config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig && ((0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isHiddenOutOfBusinessHours */ .hN)(config.settings.businessHours) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isHiddenDueToMaintenance */ .Hs)(config.settings) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isHiddenDueToConnectivity */ .VD)(config.settings, state.timedOut))) return null;
36788
- const isDisabled = config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig && ((0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isDisabledDueToMaintenance */ .$6)(config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isDisabledOutOfBusinessHours */ .dt)(config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isDisabledDueToConnectivity */ .l9)(config.settings, state.timedOut));
36789
- const isInforming = config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig && ((0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isInformingDueToMaintenance */ .iF)(config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isInformingOutOfBusinessHours */ .Gr)(config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isInformingDueToConnectivity */ .u0)(config.settings, state.timedOut));
36967
+ if (!config.active && !config.settings.embeddingConfiguration.connectivity.enabled || !config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig || config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig && ((0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isHiddenOutOfBusinessHours */ .hN)(config.settings.businessHours) || (0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isHiddenDueToMaintenance */ .Hs)(config.settings) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isHiddenDueToConnectivity */ .VD)(config.settings, state.timedOut))) return null;
36968
+ const isDisabled = config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig && ((0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isDisabledDueToMaintenance */ .$6)(config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isDisabledOutOfBusinessHours */ .dt)(config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isDisabledDueToConnectivity */ .l9)(config.settings, state.timedOut));
36969
+
36970
+ // Opt-in pause control (layout.enableIconAnimationPauseButton), shown only
36971
+ // while the animation can play: chat closed, launcher enabled and an
36972
+ // animation configured (WCAG 2.2.2 — CGY-39786).
36973
+ const showIconAnimationPauseButton = !open && !isDisabled && !!config.settings?.layout?.enableIconAnimationPauseButton && isIconAnimationConfigured(config.settings?.layout?.iconAnimation);
36974
+ const isInforming = config.isConfigLoaded && config.settings.embeddingConfiguration.awaitEndpointConfig && ((0,_webchat_helper_maintenance__WEBPACK_IMPORTED_MODULE_29__/* .isInformingDueToMaintenance */ .iF)(config.settings) || (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isInformingOutOfBusinessHours */ .Gr)(config.settings.businessHours) || (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isInformingDueToConnectivity */ .u0)(config.settings, state.timedOut));
36790
36975
  const showDisconnectOverlay = this.showDisconnectOverlay;
36791
36976
 
36792
36977
  // Drives the home-screen announcer below; the announcer mounts only
36793
36978
  // while the webchat is open, so no `open` check is needed here.
36794
36979
  const {
36795
36980
  showHomeScreenView
36796
- } = (0,_utils_screen_visibility__WEBPACK_IMPORTED_MODULE_57__/* .getScreenVisibility */ .p)(this.props, isInforming);
36981
+ } = (0,_utils_screen_visibility__WEBPACK_IMPORTED_MODULE_60__/* .getScreenVisibility */ .p)(this.props, isInforming);
36797
36982
  const openChatAriaLabel = () => {
36798
36983
  if (open) return config.settings.customTranslations?.ariaLabels?.closeChat ?? "Close chat";
36799
36984
  const openChat = config.settings.customTranslations?.ariaLabels?.openChat ?? "Open chat";
@@ -36813,7 +36998,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36813
36998
  return this.props.config.settings.maintenance.text || "This chat is currently disabled due to maintenance";
36814
36999
  } else if (isDisabled && (0,_webchat_helper_businessHours__WEBPACK_IMPORTED_MODULE_28__/* .isDisabledOutOfBusinessHours */ .dt)(this.props.config.settings.businessHours)) {
36815
37000
  return this.props.config.settings.maintenance.text || "This chat is disabled out of our business hours";
36816
- } else if (isDisabled && (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_31__/* .isDisabledDueToConnectivity */ .l9)(this.props.config.settings, this.state.timedOut)) {
37001
+ } else if (isDisabled && (0,_webchat_helper_connectivity__WEBPACK_IMPORTED_MODULE_33__/* .isDisabledDueToConnectivity */ .l9)(this.props.config.settings, this.state.timedOut)) {
36817
37002
  return this.props.config.settings.embeddingConfiguration.connectivity.text || "This chat is disabled due to connectivity issues";
36818
37003
  }
36819
37004
  };
@@ -36868,28 +37053,28 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36868
37053
  el.removeAttribute("inert");
36869
37054
  }
36870
37055
  }
36871
- }, !fullscreenMessage ? this.renderRegularLayout(isInforming) : this.renderFullscreenMessageLayout()), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_53__/* .StatusLiveRegion */ .t, null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_HomeScreenAnnouncer__WEBPACK_IMPORTED_MODULE_55__/* ["default"] */ .A, {
37056
+ }, !fullscreenMessage ? this.renderRegularLayout(isInforming) : this.renderFullscreenMessageLayout()), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_55__/* .StatusLiveRegion */ .t, null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_HomeScreenAnnouncer__WEBPACK_IMPORTED_MODULE_57__/* ["default"] */ .A, {
36872
37057
  active: showHomeScreenView,
36873
37058
  label: config.settings.customTranslations?.ariaLabels?.homeScreen ?? "Chat window home screen"
36874
- }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_DisconnectOverlay__WEBPACK_IMPORTED_MODULE_20__/* ["default"] */ .A, {
37059
+ }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_FileUploadAnnouncer__WEBPACK_IMPORTED_MODULE_58__/* ["default"] */ .A, null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_DisconnectOverlay__WEBPACK_IMPORTED_MODULE_20__/* ["default"] */ .A, {
36875
37060
  isOpen: showDisconnectOverlay,
36876
37061
  onConnect: onConnect,
36877
37062
  isPermanent: !!reconnectionLimit,
36878
37063
  isConnecting: !!this.props.connecting,
36879
37064
  onClose: handleClose,
36880
37065
  config: config
36881
- })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_UnreadMessagesAnnouncer__WEBPACK_IMPORTED_MODULE_44__/* ["default"] */ .A, {
37066
+ })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_UnreadMessagesAnnouncer__WEBPACK_IMPORTED_MODULE_46__/* ["default"] */ .A, {
36882
37067
  active: config.settings.unreadMessages.enableBadge || config.settings.unreadMessages.enableIndicator,
36883
37068
  count: unseenMessages.length,
36884
37069
  singularText: config.settings.customTranslations?.ariaLabels?.unreadMessageSingularText ?? "One unread message in chat.",
36885
37070
  pluralText: config.settings.customTranslations?.ariaLabels?.unreadMessagePluralText ?? "unread messages in chat."
36886
- }), !disableToggleButton && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_TeaserMessageAnnouncer__WEBPACK_IMPORTED_MODULE_43__/* ["default"] */ .A, {
37071
+ }), !disableToggleButton && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_TeaserMessageAnnouncer__WEBPACK_IMPORTED_MODULE_45__/* ["default"] */ .A, {
36887
37072
  active: !!lastUnseenMessageText && !open,
36888
37073
  text: lastUnseenMessageText,
36889
37074
  label: config.settings.customTranslations?.ariaLabels?.newMessagePreview ?? "New message preview"
36890
37075
  }),
36891
37076
  // Show the message teaser if there is a last bot message and the webchat is closed
36892
- lastUnseenMessageText && !open && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_TeaserMessage__WEBPACK_IMPORTED_MODULE_42__/* .TeaserMessage */ .I, {
37077
+ lastUnseenMessageText && !open && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_TeaserMessage__WEBPACK_IMPORTED_MODULE_44__/* .TeaserMessage */ .I, {
36893
37078
  messageText: lastUnseenMessageText,
36894
37079
  onClick: this.openConversationFromTeaser,
36895
37080
  config: config,
@@ -36917,7 +37102,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36917
37102
  onClick: this.handleFabClick
36918
37103
  }, webchatToggleProps, {
36919
37104
  type: "button",
36920
- className: classnames__WEBPACK_IMPORTED_MODULE_58___default()("webchat-toggle-button burst", config.settings?.layout?.iconAnimation),
37105
+ className: classnames__WEBPACK_IMPORTED_MODULE_61___default()("webchat-toggle-button burst", config.settings?.layout?.iconAnimation),
36921
37106
  id: "webchatWindowToggleButton",
36922
37107
  "aria-label": openChatAriaLabel(),
36923
37108
  ref: this.chatToggleButtonRef,
@@ -36937,7 +37122,12 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
36937
37122
  _content: unseenMessages.length,
36938
37123
  className: "webchat-unread-message-badge",
36939
37124
  "aria-hidden": "true"
36940
- }) : null))))))));
37125
+ }) : null), showIconAnimationPauseButton && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_IconAnimationPauseButton__WEBPACK_IMPORTED_MODULE_31__/* ["default"] */ .A, {
37126
+ paused: this.state.isIconAnimationPaused,
37127
+ onToggle: this.handleToggleIconAnimationPause,
37128
+ pauseLabel: config.settings.customTranslations?.ariaLabels?.pauseWebchatToggleAnimation ?? "Pause webchat toggle animation",
37129
+ resumeLabel: config.settings.customTranslations?.ariaLabels?.resumeWebchatToggleAnimation ?? "Resume webchat toggle animation"
37130
+ }))))))));
36941
37131
  }
36942
37132
  renderRegularLayout(isInforming) {
36943
37133
  const {
@@ -37054,24 +37244,25 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37054
37244
  }
37055
37245
  };
37056
37246
  const getRegularLayoutContent = () => {
37057
- if (showInformationMessage) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_InformationMessage__WEBPACK_IMPORTED_MODULE_36__/* .InformationMessage */ .E, {
37247
+ if (showInformationMessage) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_InformationMessage__WEBPACK_IMPORTED_MODULE_38__/* .InformationMessage */ .E, {
37058
37248
  message: informMessage
37059
37249
  });
37060
- if (!hasAcceptedTerms && config.settings.privacyNotice.enabled) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_PrivacyNotice__WEBPACK_IMPORTED_MODULE_37__/* .PrivacyNotice */ .r, {
37250
+ if (!hasAcceptedTerms && config.settings.privacyNotice.enabled) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_PrivacyNotice__WEBPACK_IMPORTED_MODULE_39__/* .PrivacyNotice */ .r, {
37061
37251
  privacyNotice: config.settings.privacyNotice,
37062
37252
  onAcceptTerms: handleAcceptTerms,
37063
37253
  isHomeScreenEnabled: isHomeScreenEnabled
37064
37254
  });
37065
- if (isXAppOverlayOpen) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_functional_xapp_overlay_XAppOverlay__WEBPACK_IMPORTED_MODULE_45__/* ["default"] */ .A, null);
37066
- if (showPrevConversations) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_previous_conversations_ConversationsList__WEBPACK_IMPORTED_MODULE_33__/* .PrevConversationsList */ .K, {
37255
+ if (isXAppOverlayOpen) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_functional_xapp_overlay_XAppOverlay__WEBPACK_IMPORTED_MODULE_47__/* ["default"] */ .A, null);
37256
+ if (showPrevConversations) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_previous_conversations_ConversationsList__WEBPACK_IMPORTED_MODULE_35__/* .PrevConversationsList */ .K, {
37067
37257
  conversations: this.props.prevConversations,
37068
37258
  onSetShowPrevConversations: onSetShowPrevConversations,
37069
37259
  onSwitchSession: onSwitchSession,
37070
37260
  config: config,
37071
37261
  currentSession: currentSession,
37072
- startNewConversationButtonRef: this.startNewConversationButtonRef
37262
+ startNewConversationButtonRef: this.startNewConversationButtonRef,
37263
+ emptyListTextRef: this.emptyPrevConversationsTextRef
37073
37264
  });
37074
- if (showChatOptionsScreen || showRatingScreen) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_chat_options_ChatOptions__WEBPACK_IMPORTED_MODULE_38__/* .ChatOptions */ .b, {
37265
+ if (showChatOptionsScreen || showRatingScreen) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_chat_options_ChatOptions__WEBPACK_IMPORTED_MODULE_40__/* .ChatOptions */ .b, {
37075
37266
  config: config,
37076
37267
  ratingTitleText: customRatingTitle || config.settings.chatOptions.rating.title,
37077
37268
  ratingCommentText: customRatingCommentText || config.settings.chatOptions.rating.commentPlaceholder,
@@ -37088,7 +37279,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37088
37279
  });
37089
37280
  }
37090
37281
  });
37091
- if (isDropZoneVisible) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_plugins_input_file_DropZone__WEBPACK_IMPORTED_MODULE_40__/* ["default"] */ .A, {
37282
+ if (isDropZoneVisible) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_plugins_input_file_DropZone__WEBPACK_IMPORTED_MODULE_42__/* ["default"] */ .A, {
37092
37283
  layoutSettings: config.settings.layout,
37093
37284
  dropzoneText: config.settings.fileStorageSettings?.dropzoneText
37094
37285
  });
@@ -37101,11 +37292,11 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37101
37292
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement("h3", {
37102
37293
  className: "sr-only",
37103
37294
  id: "webchatChatHistoryHeading"
37104
- }, config.settings.customTranslations?.ariaLabels?.chatHistory ?? "Chat history"), this.renderHistory()), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_ScreenReaderLiveRegion__WEBPACK_IMPORTED_MODULE_52__/* ["default"] */ .A, {
37295
+ }, config.settings.customTranslations?.ariaLabels?.chatHistory ?? "Chat history"), this.renderHistory()), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_ScreenReaderLiveRegion__WEBPACK_IMPORTED_MODULE_54__/* ["default"] */ .A, {
37105
37296
  liveContent: this.state.liveContent,
37106
37297
  intro: this.getNoticeIntro(),
37107
37298
  onIntroAnnounced: this.handleNoticeIntroAnnounced
37108
- }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_history_QueueUpdates__WEBPACK_IMPORTED_MODULE_39__/* ["default"] */ .A, null), this.renderInput());
37299
+ }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_history_QueueUpdates__WEBPACK_IMPORTED_MODULE_41__/* ["default"] */ .A, null), this.renderInput());
37109
37300
  };
37110
37301
  const getTitles = () => {
37111
37302
  if (showInformationMessage && informTitle) {
@@ -37132,7 +37323,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37132
37323
  const {
37133
37324
  showEnabledHomeScreen,
37134
37325
  showChatScreen
37135
- } = (0,_utils_screen_visibility__WEBPACK_IMPORTED_MODULE_57__/* .getScreenVisibility */ .p)(this.props, isInforming);
37326
+ } = (0,_utils_screen_visibility__WEBPACK_IMPORTED_MODULE_60__/* .getScreenVisibility */ .p)(this.props, isInforming);
37136
37327
  const isChatOptionsButtonVisible = config.settings.chatOptions.enabled && showChatScreen;
37137
37328
  const hideBackButton = showChatScreen && !isHomeScreenEnabled;
37138
37329
  const showDeleteAllConversationButton = !!((config.settings.privacyNotice.enabled && this.props.hasAcceptedTerms || !config.settings.privacyNotice.enabled) && config.settings.homeScreen.previousConversations.enableDeleteAllConversations && showPrevConversations && Object.keys(this.props.prevConversations).length);
@@ -37150,7 +37341,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37150
37341
  // hidden: it is static chrome with no live region, so nothing in it
37151
37342
  // announces during the exit.
37152
37343
  const isRegularLayoutActiveView = !!(!showEnabledHomeScreen || showInformationMessage);
37153
- return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(RegularLayoutRoot, null, !isXAppOverlayOpen && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_transition_group__WEBPACK_IMPORTED_MODULE_41__/* ["default"] */ .A, {
37344
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(RegularLayoutRoot, null, !isXAppOverlayOpen && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_transition_group__WEBPACK_IMPORTED_MODULE_43__/* ["default"] */ .A, {
37154
37345
  in: isRegularLayoutActiveView,
37155
37346
  timeout: 500,
37156
37347
  classNames: "slide-in",
@@ -37180,11 +37371,11 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37180
37371
  showChatScreen: showChatScreen,
37181
37372
  autoFocusScreenTitle: autoFocusScreenTitle,
37182
37373
  deleteIconColor: config.settings.customColors?.deleteAllConversationIconColor
37183
- })), !isSecondaryView && isHomeScreenEnabled && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_transition_group__WEBPACK_IMPORTED_MODULE_41__/* ["default"] */ .A, {
37374
+ })), !isSecondaryView && isHomeScreenEnabled && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_transition_group__WEBPACK_IMPORTED_MODULE_43__/* ["default"] */ .A, {
37184
37375
  in: !showHomeScreen,
37185
37376
  timeout: 500,
37186
37377
  classNames: "hidebackground"
37187
- }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_HomeScreen__WEBPACK_IMPORTED_MODULE_32__/* .HomeScreen */ .u, {
37378
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_HomeScreen__WEBPACK_IMPORTED_MODULE_34__/* .HomeScreen */ .u, {
37188
37379
  showHomeScreen: showHomeScreen,
37189
37380
  closeButtonRef: this.homeScreenCloseButtonRef,
37190
37381
  onSetShowHomeScreen: onSetShowHomeScreen,
@@ -37194,7 +37385,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37194
37385
  config: config,
37195
37386
  onEmitAnalytics: onEmitAnalytics,
37196
37387
  onSendActionButtonMessage: this.handleSendActionButtonMessage
37197
- })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_transition_group__WEBPACK_IMPORTED_MODULE_41__/* ["default"] */ .A, {
37388
+ })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_transition_group__WEBPACK_IMPORTED_MODULE_43__/* ["default"] */ .A, {
37198
37389
  in: isRegularLayoutActiveView,
37199
37390
  timeout: 500,
37200
37391
  classNames: "slide-in",
@@ -37204,20 +37395,34 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37204
37395
  className: "webchat-regular-layout-content",
37205
37396
  "aria-hidden": isRegularLayoutActiveView ? undefined : true,
37206
37397
  inert: isRegularLayoutActiveView ? undefined : ""
37207
- }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_FreezeOnExit__WEBPACK_IMPORTED_MODULE_54__/* ["default"] */ .A, {
37398
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_FreezeOnExit__WEBPACK_IMPORTED_MODULE_56__/* ["default"] */ .A, {
37208
37399
  active: isRegularLayoutActiveView
37209
- }, getRegularLayoutContent()), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_previous_conversations_DeleteAllConversations__WEBPACK_IMPORTED_MODULE_51__/* ["default"] */ .A, {
37400
+ }, getRegularLayoutContent()), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_previous_conversations_DeleteAllConversations__WEBPACK_IMPORTED_MODULE_53__/* ["default"] */ .A, {
37210
37401
  config: config,
37211
37402
  isOpen: this.state.showDeleteAllConversationsModal,
37212
37403
  onOpenChange: (open, confirmDelete) => {
37213
37404
  this.setState({
37214
37405
  showDeleteAllConversationsModal: open
37215
37406
  });
37216
- if (!open && this.deleteButtonInHeaderRef.current && !confirmDelete) {
37217
- this.deleteButtonInHeaderRef.current.focus();
37218
- } else {
37219
- this.startNewConversationButtonRef.current?.focus();
37407
+ if (open || !confirmDelete) {
37408
+ // Cancel / close: the Modal itself returns focus to
37409
+ // the element that opened it (the header delete button).
37410
+ return;
37220
37411
  }
37412
+ // Confirmed: the header delete button is gone with the
37413
+ // last conversation, so focus the now-rendered empty-state
37414
+ // text and the outcome is read out (SC 4.1.3, CGY-39786).
37415
+ // Deferred like the Modal's own focus moves: focusing a
37416
+ // node in the same task that inserted it is not reliably
37417
+ // announced, and the Modal's close effect restores focus
37418
+ // to whatever was active when it opened (the header title
37419
+ // after a pointer click on browsers that do not focus
37420
+ // buttons on click) — this must run after that.
37421
+ if (this.deleteAllFocusTimeout) clearTimeout(this.deleteAllFocusTimeout);
37422
+ this.deleteAllFocusTimeout = setTimeout(() => {
37423
+ this.deleteAllFocusTimeout = null;
37424
+ (this.emptyPrevConversationsTextRef.current ?? this.startNewConversationButtonRef.current)?.focus();
37425
+ }, 200);
37221
37426
  }
37222
37427
  }))));
37223
37428
  }
@@ -37265,11 +37470,11 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37265
37470
  progressiveMessageRendering
37266
37471
  } = config.settings.behavior;
37267
37472
  const isTyping = typingIndicator !== "remove" && typingIndicator !== "hide";
37268
- const isEnded = (0,_presentational_previous_conversations_helpers__WEBPACK_IMPORTED_MODULE_35__/* .isConversationEnded */ .GP)(messages);
37473
+ const isEnded = (0,_presentational_previous_conversations_helpers__WEBPACK_IMPORTED_MODULE_37__/* .isConversationEnded */ .GP)(messages);
37269
37474
 
37270
37475
  // Find privacy message and remove it from the messages list (these message types are not displayed in the chat log).
37271
37476
  // If we do not remove, it will cause the collatation of the first user message.
37272
- const messagesExcludingPrivacyMessage = (0,_utils_filter_out_control_commands__WEBPACK_IMPORTED_MODULE_48__/* ["default"] */ .A)(messages, ["acceptPrivacyPolicy"]);
37477
+ const messagesExcludingPrivacyMessage = (0,_utils_filter_out_control_commands__WEBPACK_IMPORTED_MODULE_50__/* ["default"] */ .A)(messages, ["acceptPrivacyPolicy"]);
37273
37478
 
37274
37479
  // Filter messages based on progressive rendering settings
37275
37480
  const visibleMessages = progressiveMessageRendering ? messagesExcludingPrivacyMessage.filter(message => {
@@ -37290,7 +37495,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37290
37495
  variant: "body-regular",
37291
37496
  component: "div",
37292
37497
  className: "webchat-log-ai-agent-notice-text"
37293
- }, AIAgentNoticeText || _utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_56__/* .DEFAULT_AI_AGENT_NOTICE_TEXT */ .S5), visibleMessages.map((message, index) => {
37498
+ }, AIAgentNoticeText || _utils_ai_agent_notice__WEBPACK_IMPORTED_MODULE_59__/* .DEFAULT_AI_AGENT_NOTICE_TEXT */ .S5), visibleMessages.map((message, index) => {
37294
37499
  // Lookahead if there is a user reply that includes text or attachments
37295
37500
  const hasReply = visibleMessages.slice(index + 1).some(message => {
37296
37501
  const isUser = message.source === "user";
@@ -37299,7 +37504,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37299
37504
  const hasAttachments = Array.isArray(message?.data?.attachments) && message.data.attachments.length > 0;
37300
37505
  return isUser && noControlCommands && (hasText || hasAttachments);
37301
37506
  });
37302
- return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_34__/* .Message */ .QB, {
37507
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_36__/* .Message */ .QB, {
37303
37508
  key: message.id || JSON.stringify({
37304
37509
  message,
37305
37510
  index
@@ -37320,7 +37525,7 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
37320
37525
  onSetLiveRegionText: handleLiveRegionText,
37321
37526
  "data-message-id": `webchatMessageId-${message.timestamp}`
37322
37527
  });
37323
- }), isEnded && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_34__/* .ChatEvent */ .b$, {
37528
+ }), isEnded && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_36__/* .ChatEvent */ .b$, {
37324
37529
  text: "Conversation ended",
37325
37530
  className: "webchat-chip-conversation-ended"
37326
37531
  }), enableTypingIndicator && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_history_TypingIndicator__WEBPACK_IMPORTED_MODULE_27__/* ["default"] */ .A, {
@@ -37539,6 +37744,20 @@ const Iframe = _emotion_styled__WEBPACK_IMPORTED_MODULE_2__/* ["default"] */ .A.
37539
37744
  }
37540
37745
  }));
37541
37746
 
37747
+ // Derive the canonical http(s) origin from an xApp URL, or null if the URL is
37748
+ // unparseable or uses a non-http(s) scheme. data:/about:/blob: URLs produce the
37749
+ // string "null" from new URL().origin — accepting them would let bot-supplied
37750
+ // data: xApps forge postMessages accepted by the handleSubmit origin check.
37751
+ const getXAppOrigin = url => {
37752
+ try {
37753
+ const parsed = new URL(url);
37754
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
37755
+ return parsed.origin;
37756
+ } catch {
37757
+ return null;
37758
+ }
37759
+ };
37760
+
37542
37761
  // Visually hidden but reachable by sequential focus navigation. Not
37543
37762
  // `aria-hidden`: a focusable aria-hidden node is an axe violation
37544
37763
  // (aria-hidden-focus) — and focus never rests here anyway, the onFocus
@@ -37573,6 +37792,17 @@ const xAppOverlay = () => {
37573
37792
  const rootRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
37574
37793
  const closeButtonRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
37575
37794
  const iframeRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
37795
+ const xAppOrigin = getXAppOrigin(url);
37796
+
37797
+ // For invalid or non-http(s) URLs close the overlay immediately so Redux
37798
+ // state is cleaned up and the widget does not dead-end. Dispatch must happen
37799
+ // in an effect — not during render — to satisfy React's rules.
37800
+ (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
37801
+ if (url && xAppOrigin === null) {
37802
+ console.error("[xApp] Invalid xApp URL — must be an absolute http(s) URL:", url);
37803
+ dispatch((0,_webchat_store_xapp_overlay_slice__WEBPACK_IMPORTED_MODULE_4__/* .closeOverlay */ .L9)());
37804
+ }
37805
+ }, [url, xAppOrigin]);
37576
37806
  const handleClose = () => {
37577
37807
  if (closeOnSubmit) {
37578
37808
  dispatch((0,_webchat_store_xapp_overlay_slice__WEBPACK_IMPORTED_MODULE_4__/* .closeOverlay */ .L9)());
@@ -37589,20 +37819,26 @@ const xAppOverlay = () => {
37589
37819
  dispatch((0,_webchat_store_xapp_overlay_slice__WEBPACK_IMPORTED_MODULE_4__/* .closeOverlay */ .L9)());
37590
37820
  };
37591
37821
  const handleSubmit = event => {
37592
- // WCH-SI10-004: compare canonical origins, not raw URL strings.
37593
- // The previous url.startsWith(event.origin) check was semantically backwards —
37594
- // a domain that is a string-prefix of url (e.g. "https://xapp.cognigy.a" for
37595
- // "https://xapp.cognigy.ai/form") could pass the check despite being a different
37596
- // origin. new URL(url).origin extracts the canonical scheme+host+port for an
37597
- // exact match, which is the correct cross-origin security boundary.
37598
- let urlOrigin;
37599
- try {
37600
- urlOrigin = new URL(url).origin;
37601
- } catch {
37602
- // url is empty or not a valid absolute URL — reject all postMessages.
37603
- return;
37604
- }
37605
- if (urlOrigin !== event.origin) {
37822
+ // Reuse xAppOrigin from render scope (includes the http(s)-only scheme guard
37823
+ // from getXAppOrigin). This prevents opaque-origin ("null") postMessages from
37824
+ // data:/about:/blob: URLs — which stay in Redux until the cleanup effect fires —
37825
+ // from matching via event.origin === "null" between render and effect execution.
37826
+ //
37827
+ // NOTE — known limitation: cross-origin xApps receive allow-same-origin in the
37828
+ // sandbox so their postMessages carry the real origin and this check is sound.
37829
+ // However, a cross-origin xApp document could navigate itself to the embedding
37830
+ // origin and then use frameElement to remove the sandbox (the allow-top-navigation
37831
+ // restriction covers top-level navigation only, not self-navigation). A proper fix
37832
+ // requires a token-based handshake or CSP headers on the xApp host to prevent
37833
+ // cross-origin-to-same-origin navigation; tracked as a follow-up hardening.
37834
+ //
37835
+ // Same-origin xApps omit allow-same-origin so they run with an opaque origin;
37836
+ // their postMessages arrive with event.origin === "null" and are rejected here.
37837
+ // Accepting opaque-origin messages via event.source (WindowProxy) is also unsafe:
37838
+ // a navigated iframe retains the same WindowProxy, so an attacker document loaded
37839
+ // via navigation passes both checks. Same-origin xApp x-app-submit therefore
37840
+ // requires the xApp to be hosted at a cross-origin URL.
37841
+ if (xAppOrigin === null || xAppOrigin !== event.origin) {
37606
37842
  return;
37607
37843
  }
37608
37844
  if (event.data.type !== "x-app-submit") {
@@ -37644,6 +37880,31 @@ const xAppOverlay = () => {
37644
37880
  };
37645
37881
  }, [closeOnSubmit, url, feedbackMessage]);
37646
37882
 
37883
+ // WCH-SI10-003: allow-scripts + allow-same-origin together let a same-origin
37884
+ // iframe remove its own sandbox via frameElement. Omitting allow-same-origin
37885
+ // for same-origin xApp URLs prevents that escape; the trade-off is that
37886
+ // same-origin xApp iframes run with an opaque origin, so their postMessage
37887
+ // x-app-submit events are rejected (event.origin === "null"). Tenants whose
37888
+ // xApps are hosted on the same domain as the embedding page must move them to
37889
+ // a cross-origin host to use the submit flow.
37890
+ // Cross-origin URLs include allow-same-origin so the frame can access its own
37891
+ // host's resources (cookies, localStorage) and postMessages carry the real origin.
37892
+ const isSameOrigin = xAppOrigin !== null && xAppOrigin === window.location.origin;
37893
+ const sandboxValue = ["allow-scripts",
37894
+ // Only include allow-same-origin when the URL is a valid, cross-origin http(s)
37895
+ // URL. When xAppOrigin is null the iframe is still rendered briefly before the
37896
+ // cleanup effect closes it — don't grant allow-same-origin during that window.
37897
+ ...(xAppOrigin !== null && !isSameOrigin ? ["allow-same-origin"] : []), "allow-forms", "allow-popups",
37898
+ // Popups must not inherit the creator's sandbox flags — OAuth, SSO, and
37899
+ // payment-provider windows run under constraints they were never tested against.
37900
+ "allow-popups-to-escape-sandbox", "allow-modals",
37901
+ // Boarding-pass (.pkpass), signature, PDF, and other download-generating xApps.
37902
+ "allow-downloads",
37903
+ // Redirect-based payment flows (3DS, iDEAL, Bancontact) and SSO return URLs.
37904
+ "allow-top-navigation-by-user-activation",
37905
+ // Storage Access API in a third-party frame context (Safari ITP, Chrome 3P-cookie rules).
37906
+ "allow-storage-access-by-user-activation"].join(" ");
37907
+
37647
37908
  // APG modal dialog: move focus into the dialog once on open — to the
37648
37909
  // close button (the first control, as Modal does), else the frame itself.
37649
37910
  // Deliberately NOT the title: the dialog is named by that heading, so
@@ -37717,9 +37978,10 @@ const xAppOverlay = () => {
37717
37978
  closeButtonRef: closeButtonRef
37718
37979
  }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["default"].createElement(Iframe, {
37719
37980
  ref: iframeRef,
37720
- src: url,
37981
+ src: xAppOrigin === null ? undefined : url,
37721
37982
  title: title || fallbackName,
37722
- allow: " accelerometer; ambient-light-sensor; autoplay; battery; bluetooth; camera; cross-origin-isolated; display-capture; document-domain; encrypted-media; execution-while-not-rendered; execution-while-out-of-viewport; fullscreen; gamepad; geolocation; gyroscope; hid; idle-detection; interest-cohort; local-fonts; magnetometer; microphone; midi; otp-credentials; payment; picture-in-picture; publickey-credentials-get; screen-wake-lock; serial; speaker-selection; usb; web-share; xr-spatial-tracking"
37983
+ sandbox: sandboxValue,
37984
+ allow: "autoplay; camera; display-capture; encrypted-media; fullscreen; geolocation; microphone; picture-in-picture; web-share; payment; publickey-credentials-get; otp-credentials; accelerometer; gyroscope; magnetometer; screen-wake-lock; speaker-selection"
37723
37985
  }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["default"].createElement(FocusGuard, {
37724
37986
  tabIndex: 0,
37725
37987
  onFocus: handleFocusGuard("end"),
@@ -38519,6 +38781,21 @@ class BaseInput extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38519
38781
  this.configureSpeechRecognition();
38520
38782
  }
38521
38783
  inputRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
38784
+
38785
+ /**
38786
+ * The textarea's `autoFocus` is meant for the first mount of this input
38787
+ * (entering the chat screen). The textarea also re-mounts every time the
38788
+ * persistent menu closes (the menu replaces it while open); on those
38789
+ * re-mounts it must not pull focus off the menu toggle (SC 2.4.3,
38790
+ * CGY-39786), so autoFocus is limited to the first mount.
38791
+ */
38792
+ hasMountedInput = false;
38793
+ /**
38794
+ * The deferred autofocus armed in componentDidMount. Cancelled when the
38795
+ * user operates the persistent menu before it fires — otherwise it would
38796
+ * pull focus off the toggle onto the remounted textarea — and on unmount.
38797
+ */
38798
+ autofocusTimeout = null;
38522
38799
  menuRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
38523
38800
  fileInputRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createRef();
38524
38801
 
@@ -38562,13 +38839,15 @@ class BaseInput extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38562
38839
  */
38563
38840
  restartPending = false;
38564
38841
  componentDidMount() {
38842
+ this.hasMountedInput = true;
38565
38843
  // Global handler to modify the input text
38566
38844
  window.WebChatInputTextCallback = text => {
38567
38845
  this.setState({
38568
38846
  text
38569
38847
  });
38570
38848
  };
38571
- setTimeout(() => {
38849
+ this.autofocusTimeout = setTimeout(() => {
38850
+ this.autofocusTimeout = null;
38572
38851
  // Don't pull focus away from a modal dialog the widget has opened in
38573
38852
  // the meantime (date picker, xApp overlay, Modal). Scoped to the
38574
38853
  // widget root: a dialog elsewhere on the host page (cookie banner, a
@@ -38586,6 +38865,7 @@ class BaseInput extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38586
38865
  }
38587
38866
  componentWillUnmount() {
38588
38867
  this.clearSpeechTimeout();
38868
+ this.clearAutofocusTimeout();
38589
38869
 
38590
38870
  // The input unmounts on every screen change (back to the home screen,
38591
38871
  // into the previous-conversations list). Without releasing the engine
@@ -38636,6 +38916,12 @@ class BaseInput extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38636
38916
  this.speechRecognition.lang = sttLanguage;
38637
38917
  }
38638
38918
  }
38919
+ clearAutofocusTimeout() {
38920
+ if (this.autofocusTimeout) {
38921
+ clearTimeout(this.autofocusTimeout);
38922
+ this.autofocusTimeout = null;
38923
+ }
38924
+ }
38639
38925
  clearSpeechTimeout() {
38640
38926
  if (this.speechTimeout) {
38641
38927
  clearTimeout(this.speechTimeout);
@@ -38987,20 +39273,31 @@ class BaseInput extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38987
39273
  selectionEnd: e.target.selectionEnd
38988
39274
  });
38989
39275
  };
39276
+
39277
+ // Opening or closing via the toggle does not move focus: the toggle keeps
39278
+ // it (APG disclosure pattern, SC 2.4.3 — CGY-39786). The message input
39279
+ // unmounts while the menu is open and remounts on close, but focus never
39280
+ // left the toggle, so nothing needs restoring here. The user has chosen
39281
+ // where focus is, so the initial deferred autofocus (componentDidMount)
39282
+ // must not fire afterwards and move it to the remounted textarea.
38990
39283
  togglePeristentMenu = () => {
39284
+ this.clearAutofocusTimeout();
38991
39285
  this.setState(prevState => ({
38992
39286
  isMenuOpen: !prevState.isMenuOpen
38993
- }), () => {
38994
- if (!this.state.isMenuOpen) {
38995
- if (this.inputRef.current) {
38996
- this.inputRef.current?.setSelectionRange(this.state.selectionStart, this.state.selectionEnd);
38997
- this.inputRef.current.focus();
38998
- }
38999
- }
39000
- });
39287
+ }));
39001
39288
  };
39002
39289
  onSelectPersistentMenuItem = item => {
39003
- this.togglePeristentMenu();
39290
+ // The selected item unmounts together with the menu, so focus would fall
39291
+ // back to <body>. Hand it to the message input (remounted by this state
39292
+ // change) and restore the caret position recorded on its last blur.
39293
+ this.setState({
39294
+ isMenuOpen: false
39295
+ }, () => {
39296
+ if (this.inputRef.current) {
39297
+ this.inputRef.current.setSelectionRange(this.state.selectionStart, this.state.selectionEnd);
39298
+ this.inputRef.current.focus();
39299
+ }
39300
+ });
39004
39301
  this.props.onSendMessage(item.payload, null, {
39005
39302
  label: item.title
39006
39303
  });
@@ -39083,7 +39380,7 @@ class BaseInput extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
39083
39380
  // the `disableInputAutofocus` setting.
39084
39381
  // eslint-disable-next-line jsx-a11y/no-autofocus
39085
39382
  ,
39086
- autoFocus: !disableInputAutofocus,
39383
+ autoFocus: !disableInputAutofocus && !this.hasMountedInput,
39087
39384
  value: combineStrings(text, speechInterim),
39088
39385
  onChange: this.handleChangeTextValue,
39089
39386
  onFocus: this.handleFocus,
@@ -39348,11 +39645,14 @@ const DropZone = props => {
39348
39645
  /* harmony import */ var _emotion_styled__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(84937);
39349
39646
  /* harmony import */ var _assets_close_10px_svg__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(48761);
39350
39647
  /* harmony import */ var _presentational_IconButton__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(39966);
39351
- /* harmony import */ var _presentational_LinearProgressBar__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(51308);
39352
- /* harmony import */ var _helper__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(22714);
39353
- /* harmony import */ var _cognigy_chat_components__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2854);
39354
- /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(74506);
39355
- /* harmony import */ var _webchat_store_input_input_reducer__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(16967);
39648
+ /* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(58637);
39649
+ /* harmony import */ var _presentational_LinearProgressBar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(51308);
39650
+ /* harmony import */ var _presentational_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(63813);
39651
+ /* harmony import */ var _helper__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(22714);
39652
+ /* harmony import */ var _cognigy_chat_components__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(2854);
39653
+ /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(74506);
39654
+ /* harmony import */ var _webchat_store_input_input_reducer__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(16967);
39655
+
39356
39656
 
39357
39657
 
39358
39658
 
@@ -39362,40 +39662,56 @@ const DropZone = props => {
39362
39662
 
39363
39663
 
39364
39664
 
39665
+
39666
+ const CHIP_HEIGHT = 33;
39667
+ const CHIP_GAP = 12;
39668
+ const MAX_VISIBLE_ROWS = 3;
39669
+
39670
+ // Chips wrap onto further rows instead of scrolling horizontally, so every
39671
+ // attachment stays readable at a glance. Past three rows the area scrolls
39672
+ // vertically, with the same scrollbar styling as the message textarea
39673
+ // (BaseInput.tsx) so both get restyled together.
39365
39674
  const UploadedFilesContainer = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.div(({
39366
39675
  theme
39367
39676
  }) => ({
39368
39677
  display: "flex",
39369
39678
  flexDirection: "row",
39370
- overflowX: "auto",
39371
- overflowY: "hidden",
39679
+ flexWrap: "wrap",
39372
39680
  alignItems: "center",
39373
- gap: 12,
39374
- "&::-webkit-scrollbar": {
39681
+ gap: CHIP_GAP,
39682
+ maxHeight: MAX_VISIBLE_ROWS * CHIP_HEIGHT + (MAX_VISIBLE_ROWS - 1) * CHIP_GAP,
39683
+ overflowY: "auto",
39684
+ "::-webkit-scrollbar": {
39375
39685
  width: 2,
39376
- height: 2,
39377
- visibility: "hidden"
39686
+ height: 2
39378
39687
  },
39379
- "&:hover": {
39380
- "&::-webkit-scrollbar-track": {
39381
- backgroundColor: theme.black95
39382
- },
39383
- "&::-webkit-scrollbar-thumb": {
39384
- backgroundColor: theme.black60
39385
- }
39688
+ "::-webkit-scrollbar-track": {
39689
+ backgroundColor: theme.black95
39690
+ },
39691
+ "::-webkit-scrollbar-thumb": {
39692
+ backgroundColor: theme.black60
39386
39693
  }
39387
39694
  }));
39388
39695
  const FilePreviewWrapper = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.div(({
39696
+ hasUploadError,
39389
39697
  theme
39390
39698
  }) => ({
39391
39699
  position: "relative",
39700
+ // Clips the progress bar's corners. Note a flex item with overflow:hidden
39701
+ // may shrink to 0 — the wrapping row above never asks it to, and maxWidth
39702
+ // caps long names to the FileName ellipsis instead of one huge chip.
39703
+ // A failed chip shows the failure reason instead of the name ("Upload
39704
+ // Failed: File size > 10MB" is ~160px at the default font); that text is
39705
+ // the only non-colour cue of the failure (SC 1.4.1) and must not be
39706
+ // ellipsised, so the cap is lifted to the row's width.
39392
39707
  overflow: "hidden",
39708
+ maxWidth: hasUploadError ? "100%" : 200,
39393
39709
  borderRadius: 15,
39394
- height: 33,
39395
- backgroundColor: theme.black95,
39396
- "&:focus-within": {
39397
- backgroundColor: theme.black80
39398
- }
39710
+ height: CHIP_HEIGHT,
39711
+ // A lone chip wider than its line could still shrink (and collapse its
39712
+ // name) — never shrink; maxWidth above keeps it fitting instead.
39713
+ flexShrink: 0,
39714
+ backgroundColor: theme.black95
39399
39715
  }));
39400
39716
  const UploadedFilePreview = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.div(() => ({
39401
39717
  display: "flex",
@@ -39405,7 +39721,7 @@ const UploadedFilePreview = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["def
39405
39721
  padding: "0 12px 0 6px",
39406
39722
  height: "100%"
39407
39723
  }));
39408
- const FileName = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A)(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_6__/* .Typography */ .o5)(({
39724
+ const FileName = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A)(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_8__/* .Typography */ .o5)(({
39409
39725
  hasUploadError,
39410
39726
  theme
39411
39727
  }) => ({
@@ -39415,66 +39731,104 @@ const FileName = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] *
39415
39731
  color: hasUploadError ? theme.red40 : theme.black10,
39416
39732
  alignSelf: "center"
39417
39733
  }));
39418
- const FileSize = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A)(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_6__/* .Typography */ .o5)(({
39734
+ const FileSize = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A)(_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_8__/* .Typography */ .o5)(({
39419
39735
  theme
39420
39736
  }) => ({
39421
39737
  color: theme.black40,
39422
39738
  alignSelf: "center",
39423
39739
  textWrap: "nowrap"
39424
39740
  }));
39741
+
39742
+ // Keyboard focus is shown on the button itself as two rings (same construction
39743
+ // as the launcher's FAB): an outer ring in the primary's focus variant and an
39744
+ // inner ring in that colour's computed contrast colour — white on a dark
39745
+ // primary, black on a light one. The two rings always contrast with each
39746
+ // other, so at least one of them contrasts with the chip whatever primary the
39747
+ // customer configures (SC 1.4.11 / 2.4.7). The chip background stays
39748
+ // untouched, so its texts keep their idle contrast (black40 / red40 on black95).
39425
39749
  const RemoveFileButton = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A)(_presentational_IconButton__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .A)(({
39426
39750
  theme
39427
39751
  }) => ({
39428
39752
  padding: 0,
39429
39753
  marginRight: -6,
39754
+ borderRadius: "50%",
39430
39755
  "& svg": {
39431
39756
  width: 12,
39432
39757
  height: 12
39433
39758
  },
39434
- "&:focus, &:hover": {
39759
+ "&:hover": {
39435
39760
  "& path": {
39436
- fill: `${theme.primaryColor} !important`,
39437
- outline: "none"
39761
+ fill: `${theme.primaryColor} !important`
39438
39762
  }
39763
+ },
39764
+ "&:focus-visible": {
39765
+ outline: `2px solid ${theme.primaryColorFocus}`,
39766
+ outlineOffset: 2,
39767
+ boxShadow: `0 0 0 2px ${(0,_style__WEBPACK_IMPORTED_MODULE_4__/* .getContrastColor */ .w5)(theme.primaryColorFocus, theme)}`
39439
39768
  }
39440
39769
  }));
39441
39770
  const PreviewUploadedFiles = () => {
39442
- const removeFileButtonRefs = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)([]);
39443
- const fileList = (0,react_redux__WEBPACK_IMPORTED_MODULE_7__/* .useSelector */ .d4)(state => state.input.fileList);
39444
- const removeFileAttachment = (0,react_redux__WEBPACK_IMPORTED_MODULE_7__/* .useSelector */ .d4)(state => state.config.settings.customTranslations?.ariaLabels?.removeFileAttachment);
39445
- const dispatch = (0,react_redux__WEBPACK_IMPORTED_MODULE_7__/* .useDispatch */ .wA)();
39446
- const onRemoveFileButtonClick = index => {
39447
- dispatch((0,_webchat_store_input_input_reducer__WEBPACK_IMPORTED_MODULE_8__/* .removeFileFromList */ .RB)(index));
39771
+ const fileList = (0,react_redux__WEBPACK_IMPORTED_MODULE_9__/* .useSelector */ .d4)(state => state.input.fileList);
39772
+ const ariaLabels = (0,react_redux__WEBPACK_IMPORTED_MODULE_9__/* .useSelector */ .d4)(state => state.config.settings.customTranslations?.ariaLabels);
39773
+ const dispatch = (0,react_redux__WEBPACK_IMPORTED_MODULE_9__/* .useDispatch */ .wA)();
39774
+
39775
+ // Removing a chip unmounts its (focused) remove button, which would drop
39776
+ // keyboard focus to <body> (SC 2.4.3). Focus moves to the remove button of
39777
+ // the neighbouring chip instead — the one that takes the removed chip's
39778
+ // position, or the previous one when the last chip was removed — and to the
39779
+ // attach button once the list is empty, where a user would add the next
39780
+ // file (persistent-menu toggle / message input when that is not rendered).
39781
+ // Only when the removed button actually held focus, so a removal
39782
+ // triggered while focus is elsewhere does not yank it.
39783
+ const pendingFocusIndexRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
39784
+ const onRemoveFileButtonClick = (index, event) => {
39785
+ const removed = fileList[index];
39786
+ if (document.activeElement === event.currentTarget) {
39787
+ const remaining = fileList.length - 1;
39788
+ if (remaining > 0) {
39789
+ pendingFocusIndexRef.current = Math.min(index, remaining - 1);
39790
+ } else {
39791
+ // This component unmounts with the last chip, so its effect can't
39792
+ // run any more — focus the successor now. The attach button is
39793
+ // replaced by the persistent menu while that menu is open (its
39794
+ // toggle stays), and the message input is the last resort.
39795
+ const successor = document.getElementById("webchatInputMessageAttachFileButton") ?? document.getElementById("webchatInputButtonMenu") ?? document.getElementById("webchatInputMessageInputInTextMode");
39796
+ successor?.focus();
39797
+ }
39798
+ }
39799
+ dispatch((0,_webchat_store_input_input_reducer__WEBPACK_IMPORTED_MODULE_10__/* .removeFileFromList */ .RB)(index));
39800
+ // The chip disappearing is the only visible feedback; say so (SC 4.1.3).
39801
+ if (removed) {
39802
+ (0,_presentational_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_6__/* .announceStatus */ .a)((0,_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_8__/* .formatTemplate */ .sN)(ariaLabels?.fileAttachmentRemoved ?? "{fileName} removed", {
39803
+ fileName: removed.file.name
39804
+ }));
39805
+ }
39448
39806
  };
39807
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
39808
+ const index = pendingFocusIndexRef.current;
39809
+ if (index === null) return;
39810
+ pendingFocusIndexRef.current = null;
39811
+ document.querySelector(`#filePreview${index} button`)?.focus();
39812
+ }, [fileList]);
39449
39813
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(UploadedFilesContainer, null, fileList?.map((item, index) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(FilePreviewWrapper, {
39450
39814
  key: index,
39451
- id: `filePreview${index}`
39815
+ id: `filePreview${index}`,
39816
+ hasUploadError: item.hasUploadError
39452
39817
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(UploadedFilePreview, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(RemoveFileButton, {
39453
39818
  type: "button",
39454
- onClick: () => onRemoveFileButtonClick(index),
39455
- onFocus: () => {
39456
- if (removeFileButtonRefs.current[index]) {
39457
- removeFileButtonRefs.current[index].scrollIntoView({
39458
- behavior: "smooth",
39459
- inline: "center",
39460
- block: "nearest"
39461
- });
39462
- }
39463
- },
39464
- "aria-label": `${removeFileAttachment ?? "Remove file attachment"} ${index + 1}`,
39465
- ref: ref => {
39466
- if (ref) {
39467
- removeFileButtonRefs.current[index] = ref;
39468
- }
39469
- }
39819
+ onClick: event => onRemoveFileButtonClick(index, event)
39820
+ // Position + file name: the chip's own text is not part of the
39821
+ // button's name, and a failed chip shows the reason, not the name.
39822
+ ,
39823
+ "aria-label": `${ariaLabels?.removeFileAttachment ?? "Remove file attachment"} ${index + 1}, ${item.file.name}`
39470
39824
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_assets_close_10px_svg__WEBPACK_IMPORTED_MODULE_2__/* ["default"] */ .A, null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(FileName, {
39471
39825
  component: "span",
39472
39826
  variant: "title2-regular",
39473
39827
  hasUploadError: item.hasUploadError
39474
- }, !item.hasUploadError ? `${(0,_helper__WEBPACK_IMPORTED_MODULE_5__/* .getFileName */ .W)(item.file.name)}${(0,_helper__WEBPACK_IMPORTED_MODULE_5__/* .getFileExtension */ .Q)(item.file.name)}` : item.uploadErrorReason), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(FileSize, {
39828
+ }, !item.hasUploadError ? `${(0,_helper__WEBPACK_IMPORTED_MODULE_7__/* .getFileName */ .W)(item.file.name)}${(0,_helper__WEBPACK_IMPORTED_MODULE_7__/* .getFileExtension */ .Q)(item.file.name)}` : item.uploadErrorReason), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(FileSize, {
39475
39829
  component: "span",
39476
39830
  variant: "title2-regular"
39477
- }, item.file.size > 1000000 ? `${(item.file.size / 1000000).toFixed(2)} MB` : `${(item.file.size / 1000).toFixed(2)} KB`)), item.progressPercentage && item.progressPercentage !== 100 && !item.hasUploadError && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_LinearProgressBar__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .A, {
39831
+ }, item.file.size > 1000000 ? `${(item.file.size / 1000000).toFixed(2)} MB` : `${(item.file.size / 1000).toFixed(2)} KB`)), item.progressPercentage && item.progressPercentage !== 100 && !item.hasUploadError && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_LinearProgressBar__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A, {
39478
39832
  progressPercentage: item.progressPercentage
39479
39833
  }))));
39480
39834
  };
@@ -40158,6 +40512,21 @@ const FAB = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A)
40158
40512
  animationIterationCount: 1,
40159
40513
  animationFillMode: "both"
40160
40514
  },
40515
+ // Reveals the opt-in animation pause control (IconAnimationPauseButton, the
40516
+ // launcher's following sibling) while the launcher is hovered or keyboard
40517
+ // focused. Lives here because the stylis isolate plugin prefixes the root
40518
+ // selector in front of `&`, so the rule must start with the launcher.
40519
+ "&:hover ~ .webchat-toggle-button-animation-pause, &:focus-visible ~ .webchat-toggle-button-animation-pause": {
40520
+ opacity: 1
40521
+ },
40522
+ // SC 2.3.3 / project rule: no launcher motion under reduced motion. Same
40523
+ // selectors as the burst rules above (equal specificity, declared later),
40524
+ // so this wins; a shorter selector would lose to them.
40525
+ "@media (prefers-reduced-motion: reduce)": {
40526
+ "&.burst.bounce .iconAnimationContainer.optionActive, &.burst.swing .iconAnimationContainer.optionActive, &.burst.pulse .iconAnimationContainer.optionActive": {
40527
+ animationName: "none"
40528
+ }
40529
+ },
40161
40530
  "@keyframes bounceBurst": {
40162
40531
  "0%": {
40163
40532
  transform: "translateY(0)"
@@ -40243,6 +40612,96 @@ const FABDisabled = (0,_emotion_styled__WEBPACK_IMPORTED_MODULE_0__/* ["default"
40243
40612
 
40244
40613
  /***/ },
40245
40614
 
40615
+ /***/ 32735
40616
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
40617
+
40618
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
40619
+ /* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__)
40620
+ /* harmony export */ });
40621
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
40622
+ /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(74506);
40623
+ /* harmony import */ var _cognigy_chat_components__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2854);
40624
+ /* harmony import */ var _StatusLiveRegion__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(63813);
40625
+
40626
+
40627
+
40628
+
40629
+
40630
+ // Keyed by the File object, which stays the same across every snapshot the
40631
+ // middleware dispatches, and disappears with the chip (removed or sent) — so
40632
+ // nothing is announced twice and nothing is kept alive. Module-level like the
40633
+ // StatusLiveRegion listeners: one widget instance per page.
40634
+ const announcedFiles = new WeakSet();
40635
+
40636
+ // An attachment has an outcome once it failed (any reason), was cancelled by
40637
+ // removal, or finished uploading; 30/50 are the in-flight progress values.
40638
+ const hasSettled = item => Boolean(item.hasUploadError || item.isCancelled || item.progressPercentage === 100);
40639
+
40640
+ /**
40641
+ * Announces the outcome of file uploads through the status live region
40642
+ * (WCAG 4.1.3 Status Messages, techniques ARIA22 / ARIA19). Sighted users see
40643
+ * the attachment chip drop its progress bar, or turn red with the reason
40644
+ * ("Upload Failed", "Upload Failed: File size > 10MB", "Upload Failed:
40645
+ * Infected File"); neither reaches a screen reader,
40646
+ * and the failure additionally disables Send without saying why.
40647
+ *
40648
+ * Announced politely via <StatusLiveRegion> (role="status") — deliberately
40649
+ * not through role="alert": the user has just triggered the upload and is
40650
+ * waiting for its result, so a polite message is voiced promptly anyway, and
40651
+ * an assertive one may clear the queued announcement of an incoming chat
40652
+ * message (ARIA 1.2 lets assistive technology drop queued polite changes when
40653
+ * an assertive one arrives). This matches every other error in the widget
40654
+ * (connection and speech-recognition notifications), see docs/accessibility.md.
40655
+ *
40656
+ * Each attachment is announced as soon as it has an outcome — a file
40657
+ * rejected up front (too large) at once, even while a sibling is still
40658
+ * uploading: that rejection already disables Send, and the sibling's upload
40659
+ * has no timeout, so waiting for it could delay the announcement without
40660
+ * bound. Outcomes that land in the same store update are still read as one
40661
+ * message, failures first (file name + the reason shown in the chip), then
40662
+ * the successes as one count — several uploads finishing together, or every
40663
+ * file failing on the token request, stay a single sentence. Successive
40664
+ * messages are safe because <StatusLiveRegion> appends rather than replaces
40665
+ * (see SrOnlyLiveRegion's "append" mode), so an earlier outcome is never
40666
+ * pulled from under the screen reader by a later one.
40667
+ *
40668
+ * The middleware mutates the file items in place and re-dispatches snapshots
40669
+ * of the same array (+100ms and once the uploads settle), so the outcome is
40670
+ * derived from the items' fields on every store update rather than from the
40671
+ * array identity.
40672
+ */
40673
+ const FileUploadAnnouncer = () => {
40674
+ const fileList = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__/* .useSelector */ .d4)(state => state.input.fileList);
40675
+ const ariaLabels = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__/* .useSelector */ .d4)(state => state.config.settings.customTranslations?.ariaLabels);
40676
+ // Re-evaluated on every dispatch; a string, so unchanged outcomes never re-render.
40677
+ const outcomeSignature = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__/* .useSelector */ .d4)(state => state.input.fileList.map(item => `${item.hasUploadError ? "e" : ""}${item.isCancelled ? "c" : ""}${item.progressPercentage ?? ""}`).join("|"));
40678
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
40679
+ const pending = fileList.filter(item => hasSettled(item) && !item.isCancelled && !announcedFiles.has(item.file));
40680
+ if (!pending.length) return;
40681
+ pending.forEach(item => announcedFiles.add(item.file));
40682
+ const failed = pending.filter(item => item.hasUploadError);
40683
+ const uploaded = pending.filter(item => !item.hasUploadError);
40684
+ const segments = failed.map(item => (0,_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_2__/* .formatTemplate */ .sN)(ariaLabels?.fileAttachmentFailed ?? "{fileName}: {reason}", {
40685
+ fileName: item.file.name,
40686
+ reason: item.uploadErrorReason ?? ""
40687
+ }));
40688
+ if (uploaded.length === 1) {
40689
+ segments.push((0,_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_2__/* .formatTemplate */ .sN)(ariaLabels?.fileAttachmentUploaded ?? "{fileName} attached", {
40690
+ fileName: uploaded[0].file.name
40691
+ }));
40692
+ } else if (uploaded.length > 1) {
40693
+ segments.push((0,_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_2__/* .formatTemplate */ .sN)(ariaLabels?.fileAttachmentsUploaded ?? "{count} files attached", {
40694
+ count: String(uploaded.length)
40695
+ }));
40696
+ }
40697
+ (0,_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_3__/* .announceStatus */ .a)(segments.join(". "));
40698
+ }, [fileList, outcomeSignature, ariaLabels]);
40699
+ return null;
40700
+ };
40701
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FileUploadAnnouncer);
40702
+
40703
+ /***/ },
40704
+
40246
40705
  /***/ 97475
40247
40706
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
40248
40707
 
@@ -40845,6 +41304,108 @@ const HomeScreenAnnouncer = ({
40845
41304
 
40846
41305
  /***/ },
40847
41306
 
41307
+ /***/ 71374
41308
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
41309
+
41310
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
41311
+ /* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__)
41312
+ /* harmony export */ });
41313
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
41314
+ /* harmony import */ var _emotion_styled__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(84937);
41315
+ /* harmony import */ var _assets_pause_16px_svg__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21909);
41316
+ /* harmony import */ var _assets_play_16px_svg__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6663);
41317
+
41318
+
41319
+
41320
+
41321
+ const PauseButton = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.button(({
41322
+ theme
41323
+ }) => ({
41324
+ // Fixed like the launcher (right 20px / bottom 14px, 50x50 — see
41325
+ // embedded-webchat-styles.css) and like the teaser message: a 24x24
41326
+ // pointer target (SC 2.5.8) centred on the launcher's bottom-right edge,
41327
+ // clear of its 20px icon and of the unread badge (top-right). stylis-rtl
41328
+ // mirrors `right` to `left` for RTL pages, matching the launcher's own
41329
+ // RTL rule.
41330
+ position: "fixed",
41331
+ right: 13,
41332
+ bottom: 7,
41333
+ width: 24,
41334
+ height: 24,
41335
+ padding: 0,
41336
+ margin: 0,
41337
+ display: "flex",
41338
+ alignItems: "center",
41339
+ justifyContent: "center",
41340
+ boxSizing: "border-box",
41341
+ cursor: "pointer",
41342
+ borderRadius: "50%",
41343
+ border: `2px solid ${theme.white}`,
41344
+ backgroundColor: theme.primaryColor,
41345
+ color: theme.primaryContrastColor,
41346
+ boxShadow: theme.shadow,
41347
+ // Above the launcher (z-index 1 in the widget root's stacking context)
41348
+ zIndex: 2,
41349
+ // Fine pointers: hidden until the launcher (its preceding sibling — the
41350
+ // sibling rule lives in FAB.tsx) is hovered or keyboard-focused, or the
41351
+ // button itself is; it overlaps the launcher's edge, so the pointer can
41352
+ // move onto it without a gap. It sits beside the launcher's icon, not
41353
+ // over it, so SC 1.4.13 needs no dismiss key. Kept in the DOM and
41354
+ // focusable at all times.
41355
+ opacity: 0,
41356
+ transition: "opacity 150ms ease-in-out",
41357
+ "&:hover": {
41358
+ opacity: 1
41359
+ },
41360
+ "&:focus-visible": {
41361
+ opacity: 1,
41362
+ outline: `2px solid ${theme.primaryColorFocus}`,
41363
+ outlineOffset: 2
41364
+ },
41365
+ // No hover on touch devices: always visible (SC 2.2.2 mechanism must be
41366
+ // reachable without opening the chat window).
41367
+ "@media (hover: none)": {
41368
+ opacity: 1
41369
+ },
41370
+ // The animation itself is suppressed under reduced motion (FAB.tsx), so
41371
+ // there is nothing to pause.
41372
+ "@media (prefers-reduced-motion: reduce)": {
41373
+ display: "none"
41374
+ },
41375
+ "& svg": {
41376
+ width: 12,
41377
+ height: 12,
41378
+ display: "block"
41379
+ }
41380
+ }));
41381
+ /**
41382
+ * Pause / resume control for the launcher icon animation (WCAG 2.2.2 Pause,
41383
+ * Stop, Hide — CGY-39786). Rendered by WebchatUI as the launcher's following
41384
+ * sibling, opt-in via `layout.enableIconAnimationPauseButton`. Like the audio player's play/pause button, the
41385
+ * accessible name states the action the press will perform and switches
41386
+ * together with the icon; no `aria-pressed`, which must not be combined with
41387
+ * a changing name (APG button pattern).
41388
+ */
41389
+ const IconAnimationPauseButton = ({
41390
+ paused,
41391
+ onToggle,
41392
+ pauseLabel,
41393
+ resumeLabel
41394
+ }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(PauseButton, {
41395
+ type: "button",
41396
+ className: "webchat-toggle-button-animation-pause",
41397
+ id: "webchatIconAnimationPauseButton",
41398
+ "aria-label": paused ? resumeLabel : pauseLabel,
41399
+ onClick: onToggle
41400
+ }, paused ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_assets_play_16px_svg__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .A, {
41401
+ "aria-hidden": true
41402
+ }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_assets_pause_16px_svg__WEBPACK_IMPORTED_MODULE_2__/* ["default"] */ .A, {
41403
+ "aria-hidden": true
41404
+ }));
41405
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (IconAnimationPauseButton);
41406
+
41407
+ /***/ },
41408
+
40848
41409
  /***/ 39966
40849
41410
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
40850
41411
 
@@ -41695,6 +42256,21 @@ const ScreenReaderLiveRegion = ({
41695
42256
  // window later would otherwise read long-gone status text; removing the node
41696
42257
  // is silent (removals are not announced under aria-relevant="additions text").
41697
42258
  const CLEAR_DELAY_MS = 15000;
42259
+
42260
+ /**
42261
+ * How successive messages share the region:
42262
+ * - "replace" (default): one node at a time — the next message swaps the
42263
+ * node out. Right for regions whose messages supersede each other (chat
42264
+ * log, typing indicator, unread count, teaser).
42265
+ * - "append": every message gets its own node, kept until its own 15s clear,
42266
+ * and the region is not atomic so only the added node is voiced. Right for
42267
+ * independent status messages that may land moments apart: a swap removes
42268
+ * the previous node, and NVDA drops a queued polite announcement whose
42269
+ * node left the DOM before it was voiced — the earlier message would be
42270
+ * lost. The clear is per message, so browsing users still never meet
42271
+ * text older than 15s.
42272
+ */
42273
+
41698
42274
  /**
41699
42275
  * Shared visually-hidden live region body (WCAG 4.1.3 Status Messages).
41700
42276
  *
@@ -41704,8 +42280,8 @@ const CLEAR_DELAY_MS = 15000;
41704
42280
  * cleared from the DOM after 15s.
41705
42281
  *
41706
42282
  * Used by <ScreenReaderLiveRegion> (chat messages) and <StatusLiveRegion>
41707
- * (toasts + screen changes); they stay separate DOM regions so simultaneous
41708
- * announcements queue instead of overwriting each other.
42283
+ * (toasts + screen changes + upload outcomes); they stay separate DOM regions
42284
+ * so simultaneous announcements queue instead of overwriting each other.
41709
42285
  *
41710
42286
  * The text is deliberately committed via state in an effect — one commit
41711
42287
  * after the `message` prop changes — rather than rendered directly from the
@@ -41716,26 +42292,48 @@ const CLEAR_DELAY_MS = 15000;
41716
42292
  const SrOnlyLiveRegion = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({
41717
42293
  id,
41718
42294
  message,
41719
- role
42295
+ role,
42296
+ mode = "replace"
41720
42297
  }, ref) => {
41721
- const [displayed, setDisplayed] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
42298
+ const [displayed, setDisplayed] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
42299
+ // append mode: one clear timer per message, all cancelled on unmount
42300
+ const clearTimersRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(new Map());
41722
42301
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
41723
- setDisplayed(message);
42302
+ if (mode === "replace") {
42303
+ setDisplayed(message ? [message] : []);
42304
+ if (!message) return;
42305
+ const clearTimer = setTimeout(() => setDisplayed([]), CLEAR_DELAY_MS);
42306
+ return () => clearTimeout(clearTimer);
42307
+ }
41724
42308
  if (!message) return;
41725
- const clearTimer = setTimeout(() => setDisplayed(null), CLEAR_DELAY_MS);
41726
- return () => clearTimeout(clearTimer);
41727
- }, [message]);
42309
+ const {
42310
+ id: messageId
42311
+ } = message;
42312
+ setDisplayed(previous => previous.some(item => item.id === messageId) ? previous : [...previous, message]);
42313
+ const clearTimer = setTimeout(() => {
42314
+ clearTimersRef.current.delete(messageId);
42315
+ setDisplayed(previous => previous.filter(item => item.id !== messageId));
42316
+ }, CLEAR_DELAY_MS);
42317
+ clearTimersRef.current.set(messageId, clearTimer);
42318
+ }, [message, mode]);
42319
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
42320
+ const clearTimers = clearTimersRef.current;
42321
+ return () => {
42322
+ clearTimers.forEach(clearTimeout);
42323
+ clearTimers.clear();
42324
+ };
42325
+ }, []);
41728
42326
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement("div", {
41729
42327
  ref: ref,
41730
42328
  role: role,
41731
42329
  "aria-live": "polite",
41732
42330
  "aria-relevant": "additions text",
41733
- "aria-atomic": "true",
42331
+ "aria-atomic": mode === "replace" ? "true" : "false",
41734
42332
  id: id,
41735
42333
  className: "sr-only"
41736
- }, displayed && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement("div", {
41737
- key: displayed.id
41738
- }, displayed.text));
42334
+ }, displayed.map(item => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement("div", {
42335
+ key: item.id
42336
+ }, item.text)));
41739
42337
  });
41740
42338
  SrOnlyLiveRegion.displayName = "SrOnlyLiveRegion";
41741
42339
 
@@ -41761,14 +42359,29 @@ SrOnlyLiveRegion.displayName = "SrOnlyLiveRegion";
41761
42359
  // mounted instance would announce every other instance's statuses.
41762
42360
 
41763
42361
  const statusListeners = new Set();
42362
+ const pendingAnnouncements = new Set();
41764
42363
  let statusCounter = 0;
41765
42364
 
41766
42365
  /**
41767
42366
  * Announces arbitrary status text (e.g. a screen change) through the
41768
42367
  * <StatusLiveRegion>. No-op while the region is not mounted (webchat closed).
42368
+ *
42369
+ * `delayMs` defers the announcement, e.g. past a focus move that would
42370
+ * otherwise cancel it. Pending delayed announcements belong to the region:
42371
+ * they are dropped when it unmounts (webchat closed), so a result from a
42372
+ * previous chat window is never voiced in the next one — and the caller
42373
+ * (typically a screen that unmounts right away) owns no timer.
41769
42374
  */
41770
- function announceStatus(text) {
41771
- statusListeners.forEach(listener => listener(text));
42375
+ function announceStatus(text, options) {
42376
+ if (!options?.delayMs) {
42377
+ statusListeners.forEach(listener => listener(text));
42378
+ return;
42379
+ }
42380
+ const handle = setTimeout(() => {
42381
+ pendingAnnouncements.delete(handle);
42382
+ statusListeners.forEach(listener => listener(text));
42383
+ }, options.delayMs);
42384
+ pendingAnnouncements.add(handle);
41772
42385
  }
41773
42386
 
41774
42387
  /**
@@ -41784,7 +42397,14 @@ function announceStatus(text) {
41784
42397
  * role="status" wrapper) to the DOM only when the notification fires,
41785
42398
  * which live-region processing ignores — so toasts are mirrored here and
41786
42399
  * silenced at the source (see silencedAriaProps in Notifications.tsx);
41787
- * - direct announcements via announceStatus() (e.g. "home screen appeared").
42400
+ * - direct announcements via announceStatus() (e.g. "home screen appeared",
42401
+ * file upload outcomes and removals).
42402
+ *
42403
+ * Renders in "append" mode: these messages are independent of each other
42404
+ * and can land moments apart (a rejected attachment, then its sibling's
42405
+ * upload finishing), so each keeps its own node until its own 15s clear
42406
+ * instead of being swapped out by the next — a swap would let a screen
42407
+ * reader drop the earlier one before voicing it. See SrOnlyLiveRegion.
41788
42408
  */
41789
42409
  const StatusLiveRegion = () => {
41790
42410
  const {
@@ -41800,6 +42420,8 @@ const StatusLiveRegion = () => {
41800
42420
  statusListeners.add(listener);
41801
42421
  return () => {
41802
42422
  statusListeners.delete(listener);
42423
+ pendingAnnouncements.forEach(clearTimeout);
42424
+ pendingAnnouncements.clear();
41803
42425
  };
41804
42426
  }, []);
41805
42427
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
@@ -41826,6 +42448,7 @@ const StatusLiveRegion = () => {
41826
42448
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_SrOnlyLiveRegion__WEBPACK_IMPORTED_MODULE_2__/* .SrOnlyLiveRegion */ .Y, {
41827
42449
  id: "webchatStatusLiveRegion",
41828
42450
  role: "status",
42451
+ mode: "append",
41829
42452
  message: status
41830
42453
  });
41831
42454
  };
@@ -42540,6 +43163,8 @@ const ChatOptionsFooter = props => {
42540
43163
  /* harmony import */ var _webchat_store_messages_message_reducer__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(22284);
42541
43164
  /* harmony import */ var _Modal_DeleteConfirmModal__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(75691);
42542
43165
  /* harmony import */ var _webchat_store_options_options__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(48242);
43166
+ /* harmony import */ var _StatusLiveRegion__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(63813);
43167
+
42543
43168
 
42544
43169
 
42545
43170
 
@@ -42551,6 +43176,13 @@ const ChatOptionsFooter = props => {
42551
43176
 
42552
43177
 
42553
43178
 
43179
+
43180
+ // The chat screen that replaces this one autofocuses the message input 200ms
43181
+ // after it mounts; the announcement is delayed past that so it follows the
43182
+ // focus utterance instead of being cancelled by it (same reasoning as
43183
+ // HomeScreenAnnouncer's 600ms). The timer belongs to the status live region,
43184
+ // which drops it if the chat window is closed meanwhile.
43185
+ const ANNOUNCE_DELAY_MS = 600;
42554
43186
  const Container = _emotion_styled__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */ .A.div({
42555
43187
  display: "flex",
42556
43188
  flexDirection: "column",
@@ -42602,6 +43234,12 @@ const DeleteConversation = props => {
42602
43234
  onDeleteModalStateChange(false);
42603
43235
  deleteButtonRef.current?.focus();
42604
43236
  dispatch((0,_webchat_store_ui_ui_reducer__WEBPACK_IMPORTED_MODULE_5__/* .setShowChatOptionsScreen */ .QE)(false));
43237
+ // Status message for the completed deletion (SC 4.1.3, CGY-39786). The
43238
+ // live region lives in the open chat window and survives this screen's
43239
+ // unmount.
43240
+ (0,_StatusLiveRegion__WEBPACK_IMPORTED_MODULE_10__/* .announceStatus */ .a)(config.settings.customTranslations?.conversation_deleted ?? "Conversation deleted", {
43241
+ delayMs: ANNOUNCE_DELAY_MS
43242
+ });
42605
43243
  };
42606
43244
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react__WEBPACK_IMPORTED_MODULE_0__["default"].Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(Container, {
42607
43245
  className: "webchat-delete-conversation-container"
@@ -42967,6 +43605,29 @@ const ConversationsList = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["defau
42967
43605
  outline: "none"
42968
43606
  }
42969
43607
  }));
43608
+
43609
+ // Programmatic focus target (tabIndex -1, not in the Tab order): after
43610
+ // "Delete all conversations" focus lands here so the outcome is read out
43611
+ // without a live region (SC 4.1.3, CGY-39786). Centred in the empty list;
43612
+ // the focus ring shows for keyboard-initiated focus (SC 2.4.7).
43613
+ const EmptyListText = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.p(({
43614
+ theme
43615
+ }) => ({
43616
+ margin: "auto",
43617
+ padding: "4px 8px",
43618
+ fontSize: 16,
43619
+ fontWeight: 400,
43620
+ lineHeight: "24px",
43621
+ textAlign: "center",
43622
+ color: theme.black10,
43623
+ "&:focus": {
43624
+ outline: "none"
43625
+ },
43626
+ "&:focus-visible": {
43627
+ outline: `2px solid ${theme.primaryColorFocus}`,
43628
+ outlineOffset: 2
43629
+ }
43630
+ }));
42970
43631
  const ConversationsListActions = _emotion_styled__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.div(({
42971
43632
  theme
42972
43633
  }) => ({
@@ -42996,7 +43657,8 @@ const PrevConversationsList = props => {
42996
43657
  onSetShowPrevConversations,
42997
43658
  onSwitchSession,
42998
43659
  currentSession,
42999
- startNewConversationButtonRef
43660
+ startNewConversationButtonRef,
43661
+ emptyListTextRef
43000
43662
  } = props;
43001
43663
 
43002
43664
  // we sort the conversation based on last message timestamp
@@ -43018,7 +43680,11 @@ const PrevConversationsList = props => {
43018
43680
  className: "webchat-prev-conversations-root"
43019
43681
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(ConversationsList, {
43020
43682
  className: "webchat-prev-conversations-content"
43021
- }, sessions.length > 0 && sessions.map((session, i) => {
43683
+ }, sessions.length === 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(EmptyListText, {
43684
+ className: "webchat-prev-conversations-empty",
43685
+ ref: emptyListTextRef,
43686
+ tabIndex: -1
43687
+ }, config.settings.homeScreen?.previousConversations?.emptyListText || "No previous conversations"), sessions.length > 0 && sessions.map((session, i) => {
43022
43688
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_ConversationsListItem__WEBPACK_IMPORTED_MODULE_2__/* .ConversationsListItem */ .Q, {
43023
43689
  key: i,
43024
43690
  index: i,
@@ -45195,8 +45861,13 @@ function setHasAcceptedTermsInStorage(browserStorage, userId) {
45195
45861
  /* unused harmony exports allowedHtmlTags, allowedHtmlAttributes */
45196
45862
  /* harmony import */ var dompurify__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(99418);
45197
45863
  /* harmony import */ var _store_store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6236);
45864
+ /* harmony import */ var _common_constants_blocked_tags__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(36886);
45865
+
45866
+
45198
45867
 
45199
45868
 
45869
+ // Re-export so existing consumers of sanitize.ts do not need a path change.
45870
+
45200
45871
 
45201
45872
  // Tags removed from the previous allow-list to align with DOMPurify's secure defaults
45202
45873
  // (WCH-SI10-001). Each tag enables a distinct attack vector:
@@ -45226,14 +45897,26 @@ const allowedHtmlTags = ["a", "abbr", "acronym", "address", "area", "article", "
45226
45897
  const allowedHtmlAttributes = ["accept", "accept-charset", "accesskey", "align", "alt", "autocomplete", "autofocus", "autoplay", "bgcolor", "border", "charset", "checked", "cite", "class", "color", "cols", "colspan", "content", "contenteditable", "controls", "coords", "data", "data-*", "datetime", "default", "dir", "dirname", "disabled", "download", "draggable", "dropzone", "enctype", "for", "form", "headers", "height", "hidden", "high", "href", "hreflang", "http-equiv", "id", "ismap", "kind", "label", "lang", "list", "loop", "low", "max", "maxlength", "media", "method", "min", "multiple", "muted", "name", "novalidate", "open", "optimum", "pattern", "placeholder", "poster", "preload", "readonly", "rel", "required", "reversed", "rows", "rowspan", "scope", "selected", "shape", "size", "sizes", "span", "spellcheck", "src", "srclang", "srcset", "start", "step", "tabindex", "title", "translate", "type", "usemap", "value", "width", "wrap"];
45227
45898
  const config = {
45228
45899
  ALLOWED_TAGS: allowedHtmlTags,
45229
- ALLOWED_ATTR: allowedHtmlAttributes
45900
+ ALLOWED_ATTR: allowedHtmlAttributes,
45901
+ // FORBID_TAGS overrides ALLOWED_TAGS inside DOMPurify — tags listed here are
45902
+ // stripped unconditionally, whether the default allow-list or a custom one is used.
45903
+ FORBID_TAGS: [..._common_constants_blocked_tags__WEBPACK_IMPORTED_MODULE_2__/* .ALWAYS_BLOCKED_TAGS */ .Z]
45230
45904
  };
45231
45905
  const sanitizeHTML = text => {
45232
45906
  const customAllowedHtmlTags = _store_store__WEBPACK_IMPORTED_MODULE_1__/* .storeRef */ .PU?.getState().config.settings.widgetSettings.customAllowedHtmlTags;
45233
- const configToUse = customAllowedHtmlTags ? {
45234
- ...config,
45235
- ALLOWED_TAGS: customAllowedHtmlTags
45236
- } : config;
45907
+ let configToUse = config;
45908
+ if (Array.isArray(customAllowedHtmlTags)) {
45909
+ // Pre-filter the tenant-supplied list before passing to DOMPurify (defence-in-depth).
45910
+ // config-reducer already strips blocked tags and non-strings at config-load time, but
45911
+ // we guard here too in case sanitizeHTML is ever called outside the normal config path.
45912
+ // Non-array values (e.g. a plain string) fall through to the default config — same
45913
+ // permissive fallback as config-reducer's sanitizeCustomAllowedHtmlTags().
45914
+ const safeTags = customAllowedHtmlTags.filter(tag => typeof tag === "string" && !_common_constants_blocked_tags__WEBPACK_IMPORTED_MODULE_2__/* .ALWAYS_BLOCKED_TAGS */ .Z.has(tag.toLowerCase().trim()));
45915
+ configToUse = {
45916
+ ...config,
45917
+ ALLOWED_TAGS: safeTags
45918
+ };
45919
+ }
45237
45920
  return dompurify__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A.sanitize(text, configToUse).toString();
45238
45921
  };
45239
45922
  const stripHtmlToInertText = text => {
@@ -45616,7 +46299,13 @@ const createConfigMiddleware = (url, overrideWebchatSettings) => store => next =
45616
46299
  /* unused harmony exports getInitialState, APPLY_WEBCHAT_SETTINGS_OVERRIDES, SET_CONFIG, UPDATE_SETTINGS */
45617
46300
  /* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77202);
45618
46301
  /* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_0__);
46302
+ /* harmony import */ var _common_constants_blocked_tags__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(36886);
45619
46303
 
46304
+
46305
+ function sanitizeCustomAllowedHtmlTags(tags) {
46306
+ if (!Array.isArray(tags)) return undefined;
46307
+ return tags.filter(tag => typeof tag === "string" && !_common_constants_blocked_tags__WEBPACK_IMPORTED_MODULE_1__/* .ALWAYS_BLOCKED_TAGS */ .Z.has(tag.toLowerCase().trim()));
46308
+ }
45620
46309
  const getInitialState = () => ({
45621
46310
  URLToken: "",
45622
46311
  initialSessionId: "",
@@ -45640,12 +46329,14 @@ const getInitialState = () => ({
45640
46329
  enableGenericHTMLStyling: false,
45641
46330
  disableHtmlContentSanitization: false,
45642
46331
  disableUrlButtonSanitization: false,
46332
+ enableIconAnimationPauseButton: false,
45643
46333
  watermark: "default",
45644
46334
  watermarkText: "Powered by Cognigy.AI",
45645
46335
  watermarkUrl: "",
45646
46336
  disableBotOutputBorder: false,
45647
46337
  botOutputMaxWidthPercentage: 73,
45648
- chatWindowWidth: 460
46338
+ chatWindowWidth: 460,
46339
+ galleryCardTitleBelowImage: false
45649
46340
  },
45650
46341
  colors: {
45651
46342
  primaryColor: "",
@@ -45843,6 +46534,12 @@ const config = (state = getInitialState(), action) => {
45843
46534
  {
45844
46535
  // deepMerge the settings since we have nested settings since v3
45845
46536
  const mergedSettings = lodash_merge__WEBPACK_IMPORTED_MODULE_0___default()({}, state.settings, action.config.settings);
46537
+
46538
+ // Strip dangerous tags from the tenant-supplied allow-list before storing so both
46539
+ // webchat's sanitizeHTML and @cognigy/chat-components receive the safe list.
46540
+ if (mergedSettings.widgetSettings?.customAllowedHtmlTags !== undefined) {
46541
+ mergedSettings.widgetSettings.customAllowedHtmlTags = sanitizeCustomAllowedHtmlTags(mergedSettings.widgetSettings.customAllowedHtmlTags);
46542
+ }
45846
46543
  return {
45847
46544
  ...state,
45848
46545
  ...action.config,
@@ -45854,6 +46551,11 @@ const config = (state = getInitialState(), action) => {
45854
46551
  {
45855
46552
  // deepMerge the settings since we have nested settings since v3
45856
46553
  const mergedSettings = lodash_merge__WEBPACK_IMPORTED_MODULE_0___default()({}, state.settings, action.payload);
46554
+
46555
+ // Same safety filter as SET_CONFIG — keep the stored list clean.
46556
+ if (mergedSettings.widgetSettings?.customAllowedHtmlTags !== undefined) {
46557
+ mergedSettings.widgetSettings.customAllowedHtmlTags = sanitizeCustomAllowedHtmlTags(mergedSettings.widgetSettings.customAllowedHtmlTags);
46558
+ }
45857
46559
  return {
45858
46560
  ...state,
45859
46561
  settings: mergedSettings
@@ -46242,6 +46944,8 @@ const inputCollation = (state = getInitialState(), action) => {
46242
46944
  /* harmony export */ });
46243
46945
  /* harmony import */ var _input_reducer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(16967);
46244
46946
  /* harmony import */ var _helper_endpoint__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11351);
46947
+ /* harmony import */ var _cognigy_chat_components__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2854);
46948
+
46245
46949
 
46246
46950
 
46247
46951
  const ADD_FILES_TO_LIST = "ADD_FILES_TO_LIST";
@@ -46257,18 +46961,34 @@ const createFileInputMiddleware = () => store => next => async action => {
46257
46961
  fileAttachmentMaxSize,
46258
46962
  embeddingConfiguration: {
46259
46963
  _endpointTokenUrl
46260
- }
46964
+ },
46965
+ customTranslations
46261
46966
  } = store.getState().config.settings;
46262
46967
  const existingFileList = store.getState().input.fileList;
46263
46968
  let newFileList = [];
46264
46969
  const fileAttachmentMaxSizeInMb = fileAttachmentMaxSize > 0 ? fileAttachmentMaxSize / (1024 * 1024) : 0;
46970
+
46971
+ // The reason is shown in the attachment chip and read out through
46972
+ // the status live region (FileUploadAnnouncer), so it is configurable
46973
+ // like every other user-facing string.
46974
+ const uploadFailedText = customTranslations?.file_upload_failed ?? "Upload Failed";
46975
+ // The specific reasons are prefixed with the generic failure text
46976
+ // ("Upload Failed: File size > 10MB", "Upload Failed: Infected
46977
+ // File"): on their own, next to the file's actual size, they read
46978
+ // as information about the file rather than as the reason it was
46979
+ // not attached, leaving the red colour as the only cue (SC 1.4.1)
46980
+ // — in the chip and in the announcement alike.
46981
+ const uploadInfectedText = `${uploadFailedText}: ${customTranslations?.file_upload_infected ?? "Infected File"}`;
46982
+ const uploadTooLargeText = `${uploadFailedText}: ${(0,_cognigy_chat_components__WEBPACK_IMPORTED_MODULE_2__/* .formatTemplate */ .sN)(customTranslations?.file_upload_too_large ?? "File size > {maxSizeInMb}MB", {
46983
+ maxSizeInMb: String(fileAttachmentMaxSizeInMb)
46984
+ })}`;
46265
46985
  action.newFiles?.forEach(file => {
46266
46986
  if (file.size > fileAttachmentMaxSize) {
46267
46987
  newFileList.push({
46268
46988
  file: file,
46269
46989
  progressPercentage: 10,
46270
46990
  hasUploadError: true,
46271
- uploadErrorReason: `File size > ${fileAttachmentMaxSizeInMb}MB`
46991
+ uploadErrorReason: uploadTooLargeText
46272
46992
  });
46273
46993
  } else {
46274
46994
  newFileList.push({
@@ -46278,6 +46998,14 @@ const createFileInputMiddleware = () => store => next => async action => {
46278
46998
  }
46279
46999
  });
46280
47000
  store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileList */ .hM)(existingFileList.concat(newFileList)));
47001
+
47002
+ // The items are mutated in place below; the later dispatches only have
47003
+ // to re-render the CURRENT list. Re-dispatching the snapshot captured
47004
+ // above would resurrect chips the user removed (or a message sent) in
47005
+ // the meantime — and a removed file must not be uploaded or flag an
47006
+ // error either.
47007
+ const isStillListed = fileItem => store.getState().input.fileList.includes(fileItem);
47008
+ const refreshFileList = () => store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileList */ .hM)([...store.getState().input.fileList]));
46281
47009
  const fileUploadTokenApiUrl = `${_endpointTokenUrl}/fileuploadtoken`;
46282
47010
  let response;
46283
47011
  let hasError = false;
@@ -46290,22 +47018,21 @@ const createFileInputMiddleware = () => store => next => async action => {
46290
47018
  if (!fileItem.hasUploadError) {
46291
47019
  fileItem.progressPercentage = 50;
46292
47020
  fileItem.hasUploadError = hasError;
46293
- fileItem.uploadErrorReason = hasError ? "Upload Failed" : fileItem.uploadErrorReason;
47021
+ fileItem.uploadErrorReason = hasError ? uploadFailedText : fileItem.uploadErrorReason;
46294
47022
  }
46295
47023
  return fileItem;
46296
47024
  });
46297
- setTimeout(() => {
46298
- store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileList */ .hM)(existingFileList.concat(newFileList)));
46299
- }, 100);
46300
- newFileList = (await Promise.all(newFileList.map(async fileItem => {
47025
+ setTimeout(refreshFileList, 100);
47026
+ await Promise.all(newFileList.map(async fileItem => {
47027
+ if (!isStillListed(fileItem)) return;
46301
47028
  try {
46302
47029
  if (!fileItem.hasUploadError && !fileItem.isCancelled) {
46303
47030
  fileItem.abortController = new AbortController();
46304
47031
  fileItem.uploadFileMeta = await (0,_helper_endpoint__WEBPACK_IMPORTED_MODULE_1__/* .uploadFile */ .QM)(fileItem.file, response.fileUploadUrl, response.token, fileItem.abortController);
46305
47032
  if (fileItem.uploadFileMeta.status === "infected") {
46306
47033
  fileItem.hasUploadError = true;
46307
- fileItem.uploadErrorReason = "Infected File";
46308
- store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileUploadError */ .AA)(true));
47034
+ fileItem.uploadErrorReason = uploadInfectedText;
47035
+ if (isStillListed(fileItem)) store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileUploadError */ .AA)(true));
46309
47036
  }
46310
47037
  fileItem.uploadFileMeta.fileName = fileItem.file.name;
46311
47038
  fileItem.progressPercentage = 100;
@@ -46318,13 +47045,12 @@ const createFileInputMiddleware = () => store => next => async action => {
46318
47045
  return;
46319
47046
  } else {
46320
47047
  fileItem.hasUploadError = true;
46321
- fileItem.uploadErrorReason = "Failed Upload!";
46322
- store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileUploadError */ .AA)(true));
47048
+ fileItem.uploadErrorReason = uploadFailedText;
47049
+ if (isStillListed(fileItem)) store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileUploadError */ .AA)(true));
46323
47050
  }
46324
47051
  }
46325
- return fileItem;
46326
- }))).filter(Boolean);
46327
- store.dispatch((0,_input_reducer__WEBPACK_IMPORTED_MODULE_0__/* .setFileList */ .hM)(existingFileList.concat(newFileList)));
47052
+ }));
47053
+ refreshFileList();
46328
47054
  break;
46329
47055
  }
46330
47056
  }
@@ -56408,9 +57134,10 @@ function _typeof(o) {
56408
57134
  /* harmony export */ Vp: () => (/* binding */ TypingIndicator),
56409
57135
  /* harmony export */ b$: () => (/* binding */ ChatEvent),
56410
57136
  /* harmony export */ o5: () => (/* binding */ Typography),
57137
+ /* harmony export */ sN: () => (/* binding */ interpolateString),
56411
57138
  /* harmony export */ wr: () => (/* binding */ ActionButtons)
56412
57139
  /* harmony export */ });
56413
- /* unused harmony exports PrimaryButton, SecondaryButton, formatTemplate, match */
57140
+ /* unused harmony exports PrimaryButton, SecondaryButton, defaultAllowedHtmlTags, match */
56414
57141
  /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(71178);
56415
57142
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(51649);
56416
57143
  /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7516);
@@ -56420,6 +57147,10 @@ function _typeof(o) {
56420
57147
  try {
56421
57148
  if (typeof document != "undefined") {
56422
57149
  var elementStyle = document.createElement("style");
57150
+ var nonce = document.head.querySelector("meta[property=csp-nonce]")?.getAttribute("content");
57151
+ if (nonce) {
57152
+ elementStyle.nonce = nonce;
57153
+ }
56423
57154
  elementStyle.appendChild(document.createTextNode(`._bubble_1v5xk_1 {
56424
57155
  border-radius: var(--cc-bubble-border-radius, 15px);
56425
57156
  border: 1px solid var(--cc-border-bot-message);
@@ -56677,7 +57408,7 @@ article.user ._outgoing_1v5xk_52 {
56677
57408
  ._icon_ak7mc_52 svg {
56678
57409
  fill: var(--cc-black-10);
56679
57410
  }
56680
- article ._wrapper_2f2tp_1 {
57411
+ article ._wrapper_tochy_1 {
56681
57412
  position: relative;
56682
57413
  border-radius: var(--cc-bubble-border-radius);
56683
57414
  max-width: 295px;
@@ -56685,53 +57416,58 @@ article ._wrapper_2f2tp_1 {
56685
57416
  outline: none;
56686
57417
  }
56687
57418
 
56688
- article ._wrapper_2f2tp_1 ._fixedImage_2f2tp_9,
56689
- article ._wrapper_2f2tp_1 ._flexImage_2f2tp_10 {
57419
+ article ._wrapper_tochy_1 ._fixedImage_tochy_9,
57420
+ article ._wrapper_tochy_1 ._flexImage_tochy_10 {
56690
57421
  border-top-left-radius: var(--cc-bubble-border-radius);
56691
57422
  border-top-right-radius: var(--cc-bubble-border-radius);
56692
57423
  }
56693
57424
 
56694
- article ._wrapper_2f2tp_1 ._fixedImage_2f2tp_9:focus-visible,
56695
- article ._wrapper_2f2tp_1 ._flexImage_2f2tp_10:focus-visible {
57425
+ /* Two-colour focus ring, as on the primary buttons and gallery links: the
57426
+ primary outline plus a 4px contrast band, so the ring stays visible whatever
57427
+ the image behind it (WCAG 2.4.7 Focus Visible, 1.4.11 Non-text Contrast). */
57428
+ article ._wrapper_tochy_1 ._fixedImage_tochy_9:focus-visible,
57429
+ article ._wrapper_tochy_1 ._flexImage_tochy_10:focus-visible {
56696
57430
  outline: 2px solid var(--cc-primary-color-focus);
57431
+ outline-offset: 2px;
57432
+ box-shadow: 0 0 0 4px var(--cc-secondary-contrast-color);
56697
57433
  }
56698
57434
 
56699
- article ._wrapper_2f2tp_1 img {
57435
+ article ._wrapper_tochy_1 img {
56700
57436
  border-radius: var(--cc-bubble-border-radius);
56701
57437
  width: 100%;
56702
57438
  display: block;
56703
57439
  outline: none;
56704
57440
  }
56705
- ._wrapper_2f2tp_1._wrapperDownloadable_2f2tp_26 img {
57441
+ ._wrapper_tochy_1._wrapperDownloadable_tochy_31 img {
56706
57442
  border-bottom-left-radius: 0px;
56707
57443
  border-bottom-right-radius: 0px;
56708
57444
  }
56709
57445
 
56710
- ._wrapperDownloadable_2f2tp_26 {
57446
+ ._wrapperDownloadable_tochy_31 {
56711
57447
  background-color: var(--cc-white);
56712
57448
  cursor: pointer;
56713
57449
  border: 1px solid var(--cc-black-80);
56714
57450
  }
56715
- ._wrapperDownloadable_2f2tp_26 img:hover,
56716
- ._wrapperDownloadable_2f2tp_26 img:focus {
57451
+ ._wrapperDownloadable_tochy_31 img:hover,
57452
+ ._wrapperDownloadable_tochy_31 img:focus {
56717
57453
  opacity: 0.8;
56718
57454
  }
56719
57455
 
56720
- ._flexImage_2f2tp_10 img {
57456
+ ._flexImage_tochy_10 img {
56721
57457
  aspect-ratio: 16/9;
56722
57458
  object-fit: cover;
56723
57459
  object-position: left;
56724
57460
  }
56725
57461
 
56726
- ._fixedImage_2f2tp_9 img {
57462
+ ._fixedImage_tochy_9 img {
56727
57463
  height: auto;
56728
57464
  }
56729
57465
 
56730
- ._downloadButtonWrapper_2f2tp_51 {
57466
+ ._downloadButtonWrapper_tochy_56 {
56731
57467
  padding: 16px;
56732
57468
  }
56733
57469
 
56734
- ._brokenImage_2f2tp_55 {
57470
+ ._brokenImage_tochy_60 {
56735
57471
  aspect-ratio: 16/9;
56736
57472
  width: 100%;
56737
57473
  display: block;
@@ -57743,19 +58479,19 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57743
58479
  ._mainButtonWrapper_zhv80_144 {
57744
58480
  padding: 16px;
57745
58481
  }
57746
- ._slideItem_fa7bu_1 {
58482
+ ._slideItem_jw04w_1 {
57747
58483
  position: relative;
57748
58484
  width: 206px;
57749
58485
  /* height: 150px; */
57750
58486
  overflow: hidden;
57751
58487
  }
57752
58488
 
57753
- ._slideItem_fa7bu_1 ._top_fa7bu_8 {
58489
+ ._slideItem_jw04w_1 ._top_jw04w_8 {
57754
58490
  position: relative;
57755
58491
  border-radius: var(--cc-bubble-border-radius);
57756
58492
  }
57757
58493
 
57758
- ._slideItem_fa7bu_1 ._bottom_fa7bu_13 {
58494
+ ._slideItem_jw04w_1 ._bottom_jw04w_13 {
57759
58495
  border-bottom-left-radius: var(--cc-bubble-border-radius);
57760
58496
  border-bottom-right-radius: var(--cc-bubble-border-radius);
57761
58497
  border: 1px solid var(--cc-black-80);
@@ -57766,7 +58502,7 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57766
58502
  padding: 8px;
57767
58503
  }
57768
58504
 
57769
- ._slideItem_fa7bu_1 ._top_fa7bu_8 img {
58505
+ ._slideItem_jw04w_1 ._top_jw04w_8 img {
57770
58506
  aspect-ratio: 206/150;
57771
58507
  object-fit: cover;
57772
58508
  object-position: left;
@@ -57776,7 +58512,7 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57776
58512
  outline: none;
57777
58513
  }
57778
58514
 
57779
- ._slideItem_fa7bu_1 ._brokenImage_fa7bu_34 {
58515
+ ._slideItem_jw04w_1 ._brokenImage_jw04w_34 {
57780
58516
  aspect-ratio: 206/150;
57781
58517
  width: 100%;
57782
58518
  display: block;
@@ -57785,9 +58521,9 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57785
58521
  background-color: var(--cc-black-80);
57786
58522
  }
57787
58523
 
57788
- ._slideItem_fa7bu_1 ._hasExtraInfo_fa7bu_43,
57789
- ._slideItem_fa7bu_1 ._hasExtraInfo_fa7bu_43 img,
57790
- ._slideItem_fa7bu_1 ._brokenImage_fa7bu_34 {
58524
+ ._slideItem_jw04w_1 ._hasExtraInfo_jw04w_43,
58525
+ ._slideItem_jw04w_1 ._hasExtraInfo_jw04w_43 img,
58526
+ ._slideItem_jw04w_1 ._brokenImage_jw04w_34 {
57791
58527
  border-bottom-left-radius: 0px;
57792
58528
  border-bottom-right-radius: 0px;
57793
58529
  }
@@ -57796,7 +58532,7 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57796
58532
  ** Default (legacy) layout: the title overlays the bottom of the image.
57797
58533
  ** Used when \`galleryCardTitleBelowImage\` is not enabled.
57798
58534
  */
57799
- ._slideItem_fa7bu_1 ._top_fa7bu_8 h4 {
58535
+ ._slideItem_jw04w_1 ._top_jw04w_8 h4 {
57800
58536
  position: absolute;
57801
58537
  margin: 0px;
57802
58538
  margin-inline-start: 8px;
@@ -57810,7 +58546,7 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57810
58546
  ** overlaid title switches to dark text there (WCAG 1.4.3 Contrast Minimum,
57811
58547
  ** CGY-37634).
57812
58548
  */
57813
- ._slideItem_fa7bu_1 ._top_fa7bu_8:has(._brokenImage_fa7bu_34) h4 {
58549
+ ._slideItem_jw04w_1 ._top_jw04w_8:has(._brokenImage_jw04w_34) h4 {
57814
58550
  color: var(--cc-black-10);
57815
58551
  }
57816
58552
 
@@ -57818,13 +58554,13 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57818
58554
  ** Opt-in layout (galleryCardTitleBelowImage): the title moves into the content
57819
58555
  ** block beneath the image, so it shares the subtitle's flush margins/padding.
57820
58556
  */
57821
- ._slideItem_fa7bu_1 ._bottom_fa7bu_13 p,
57822
- ._slideItem_fa7bu_1 ._bottom_fa7bu_13 h4 {
58557
+ ._slideItem_jw04w_1 ._bottom_jw04w_13 p,
58558
+ ._slideItem_jw04w_1 ._bottom_jw04w_13 h4 {
57823
58559
  padding: 0px;
57824
58560
  margin: 0px;
57825
58561
  }
57826
58562
 
57827
- ._slideItem_fa7bu_1 ._bottom_fa7bu_13 ._buttonListItem_fa7bu_82 {
58563
+ ._slideItem_jw04w_1 ._bottom_jw04w_13 ._buttonListItem_jw04w_82 {
57828
58564
  width: 100%;
57829
58565
  }
57830
58566
 
@@ -57833,24 +58569,45 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57833
58569
  ** block (keeping the block's column gap), or sits on the image area when the
57834
58570
  ** block has no text. Buttons are never inside it.
57835
58571
  */
57836
- ._slideItem_fa7bu_1 ._link_fa7bu_91 {
58572
+ ._slideItem_jw04w_1 ._link_jw04w_91 {
57837
58573
  display: flex;
57838
58574
  flex-direction: column;
57839
58575
  gap: 8px;
57840
58576
  }
57841
- ._slideItem_fa7bu_1 [role="link"] {
58577
+ ._slideItem_jw04w_1 [role="link"] {
57842
58578
  cursor: pointer;
57843
58579
  }
57844
- ._slideItem_fa7bu_1 [role="link"]:focus-visible {
58580
+ /* Two-colour focus ring, as on the primary buttons: the primary outline plus a
58581
+ 4px contrast band, so the ring stays visible whatever the image or theme
58582
+ colour behind it (WCAG 2.4.7 Focus Visible, 1.4.11 Non-text Contrast). */
58583
+ ._slideItem_jw04w_1 [role="link"]:focus-visible {
57845
58584
  outline: 2px solid var(--cc-primary-color-focus);
57846
58585
  outline-offset: 2px;
58586
+ box-shadow: 0 0 0 4px var(--cc-secondary-contrast-color);
57847
58587
  }
57848
58588
  /* .slideItem is overflow:hidden and the image area fills it edge to edge, so
57849
58589
  the focus ring is drawn inside the card; an outset ring there is clipped
57850
- away entirely (WCAG 2.4.7 Focus Visible). The text link sits inside the
57851
- content block's 8px padding, so its outset ring is never clipped. */
57852
- ._slideItem_fa7bu_1 ._top_fa7bu_8[role="link"]:focus-visible {
58590
+ away entirely. The text link sits inside the content block's 8px padding,
58591
+ so its outset ring is never clipped. The image-area ring is drawn by an
58592
+ overlay pseudo-element: an inset box-shadow on the element itself would be
58593
+ painted beneath the <img> that fills it, and the element's own outline is
58594
+ painted beneath its positioned pseudo-element. The outline is kept but
58595
+ transparent so forced-colors mode (which drops box-shadows and recolours
58596
+ outlines) still shows a ring. */
58597
+ ._slideItem_jw04w_1 ._top_jw04w_8[role="link"]:focus-visible {
58598
+ outline-color: transparent;
57853
58599
  outline-offset: -2px;
58600
+ box-shadow: none;
58601
+ }
58602
+ ._slideItem_jw04w_1 ._top_jw04w_8[role="link"]:focus-visible::after {
58603
+ content: "";
58604
+ position: absolute;
58605
+ inset: 0;
58606
+ border-radius: inherit;
58607
+ box-shadow:
58608
+ inset 0 0 0 2px var(--cc-primary-color-focus),
58609
+ inset 0 0 0 4px var(--cc-secondary-contrast-color);
58610
+ pointer-events: none;
57854
58611
  }
57855
58612
 
57856
58613
  /*
@@ -57859,7 +58616,7 @@ article ._wrapper_zhv80_1 ._list_zhv80_8 ._listItemRoot_zhv80_8 {
57859
58616
  ** The idea is to integrate with modules CSS in order to increase the specificity
57860
58617
  ** and avoid conflicts on consumer apps
57861
58618
  */
57862
- article .swiper._wrapper_fa7bu_117 {
58619
+ article .swiper._wrapper_jw04w_138 {
57863
58620
  margin-left: -20px;
57864
58621
  margin-right: -20px;
57865
58622
  padding-left: 20px;
@@ -57875,7 +58632,7 @@ article .swiper._wrapper_fa7bu_117 {
57875
58632
  z-index: 1;
57876
58633
  display: block;
57877
58634
  }
57878
- article .swiper._wrapper_fa7bu_117 .swiper-wrapper {
58635
+ article .swiper._wrapper_jw04w_138 .swiper-wrapper {
57879
58636
  position: relative;
57880
58637
  width: 100%;
57881
58638
  /* height: 100%; */
@@ -57885,15 +58642,15 @@ article .swiper._wrapper_fa7bu_117 .swiper-wrapper {
57885
58642
  transition-timing-function: initial;
57886
58643
  box-sizing: content-box;
57887
58644
  }
57888
- article .swiper._wrapper_fa7bu_117 .swiper-android .swiper-slide,
57889
- article .swiper._wrapper_fa7bu_117 .swiper-ios .swiper-slide,
57890
- article .swiper._wrapper_fa7bu_117 .swiper-wrapper {
58645
+ article .swiper._wrapper_jw04w_138 .swiper-android .swiper-slide,
58646
+ article .swiper._wrapper_jw04w_138 .swiper-ios .swiper-slide,
58647
+ article .swiper._wrapper_jw04w_138 .swiper-wrapper {
57891
58648
  transform: translate3d(0px, 0, 0);
57892
58649
  }
57893
- article .swiper._wrapper_fa7bu_117 .swiper-horizontal {
58650
+ article .swiper._wrapper_jw04w_138 .swiper-horizontal {
57894
58651
  touch-action: pan-y;
57895
58652
  }
57896
- article .swiper._wrapper_fa7bu_117 .swiper-slide {
58653
+ article .swiper._wrapper_jw04w_138 .swiper-slide {
57897
58654
  flex-shrink: 0;
57898
58655
  width: 100%;
57899
58656
  height: 100%;
@@ -57901,15 +58658,15 @@ article .swiper._wrapper_fa7bu_117 .swiper-slide {
57901
58658
  transition-property: transform;
57902
58659
  display: block;
57903
58660
  }
57904
- article .swiper._wrapper_fa7bu_117 .swiper-slide-invisible-blank {
58661
+ article .swiper._wrapper_jw04w_138 .swiper-slide-invisible-blank {
57905
58662
  visibility: hidden;
57906
58663
  }
57907
58664
 
57908
58665
  /*
57909
58666
  ** SWIPER PAGINATION
57910
58667
  */
57911
- article .swiper._wrapper_fa7bu_117 .gallery-button-prev,
57912
- article .swiper._wrapper_fa7bu_117 .gallery-button-next {
58668
+ article .swiper._wrapper_jw04w_138 .gallery-button-prev,
58669
+ article .swiper._wrapper_jw04w_138 .gallery-button-next {
57913
58670
  position: absolute;
57914
58671
  top: calc(150px / 2 - 8px);
57915
58672
  z-index: 10;
@@ -57922,24 +58679,24 @@ article .swiper._wrapper_fa7bu_117 .gallery-button-next {
57922
58679
  display: flex;
57923
58680
  align-items: center;
57924
58681
  }
57925
- article .swiper._wrapper_fa7bu_117 .gallery-button-prev {
58682
+ article .swiper._wrapper_jw04w_138 .gallery-button-prev {
57926
58683
  left: 20px;
57927
58684
  }
57928
- article .swiper._wrapper_fa7bu_117 .gallery-button-next {
58685
+ article .swiper._wrapper_jw04w_138 .gallery-button-next {
57929
58686
  right: 20px;
57930
58687
  transform: rotate(180deg);
57931
58688
  }
57932
- article .swiper._wrapper_fa7bu_117 .gallery-button-prev:dir(rtl) {
58689
+ article .swiper._wrapper_jw04w_138 .gallery-button-prev:dir(rtl) {
57933
58690
  left: unset;
57934
58691
  right: 20px;
57935
58692
  transform: rotate(180deg);
57936
58693
  }
57937
- article .swiper._wrapper_fa7bu_117 .gallery-button-next:dir(rtl) {
58694
+ article .swiper._wrapper_jw04w_138 .gallery-button-next:dir(rtl) {
57938
58695
  right: unset;
57939
58696
  left: 20px;
57940
58697
  transform: rotate(0deg);
57941
58698
  }
57942
- article .swiper._wrapper_fa7bu_117 .swiper-button-disabled {
58699
+ article .swiper._wrapper_jw04w_138 .swiper-button-disabled {
57943
58700
  opacity: 0;
57944
58701
  }
57945
58702
 
@@ -57953,22 +58710,22 @@ article .swiper._wrapper_fa7bu_117 .swiper-button-disabled {
57953
58710
  ** the cards. A single row adds 28px: 2px gap + 24px row + 2px container
57954
58711
  ** padding.
57955
58712
  */
57956
- article .swiper._wrapper_fa7bu_117 .swiper-pagination {
58713
+ article .swiper._wrapper_jw04w_138 .swiper-pagination {
57957
58714
  position: relative;
57958
58715
  text-align: center;
57959
58716
  transition: 300ms opacity;
57960
58717
  transform: translate3d(0, 0, 0);
57961
58718
  z-index: 10;
57962
58719
  }
57963
- article .swiper._wrapper_fa7bu_117 .swiper-pagination.swiper-pagination-hidden {
58720
+ article .swiper._wrapper_jw04w_138 .swiper-pagination.swiper-pagination-hidden {
57964
58721
  opacity: 0;
57965
58722
  }
57966
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-disabled > .swiper-pagination,
57967
- article .swiper._wrapper_fa7bu_117 .swiper-pagination.swiper-pagination-disabled {
58723
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-disabled > .swiper-pagination,
58724
+ article .swiper._wrapper_jw04w_138 .swiper-pagination.swiper-pagination-disabled {
57968
58725
  display: none !important;
57969
58726
  }
57970
- article .swiper._wrapper_fa7bu_117 .swiper-horizontal > .swiper-pagination-bullets,
57971
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullets.swiper-pagination-horizontal {
58727
+ article .swiper._wrapper_jw04w_138 .swiper-horizontal > .swiper-pagination-bullets,
58728
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullets.swiper-pagination-horizontal {
57972
58729
  margin-top: 2px;
57973
58730
  width: 100%;
57974
58731
  display: flex;
@@ -57982,7 +58739,7 @@ article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullets.swiper-pagination-
57982
58739
  ** is unchanged while the hit area grows around it; focus styling therefore
57983
58740
  ** targets ::before too.
57984
58741
  */
57985
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet {
58742
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet {
57986
58743
  /* Fixed-size flex item: the 24px target never shrinks when the row is
57987
58744
  full; overflowing bullets wrap (see .swiper-pagination). */
57988
58745
  flex: 0 0 24px;
@@ -57996,7 +58753,7 @@ article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet {
57996
58753
  background: transparent;
57997
58754
  opacity: 1;
57998
58755
  }
57999
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet::before {
58756
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet::before {
58000
58757
  content: "";
58001
58758
  width: 6px;
58002
58759
  height: 6px;
@@ -58005,21 +58762,21 @@ article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet::before {
58005
58762
  }
58006
58763
  /* Declared before the focus rules (and lower in specificity) so a focused
58007
58764
  active bullet always shows the focus colour, not the active black. */
58008
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet-active::before {
58765
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet-active::before {
58009
58766
  background-color: black;
58010
58767
  }
58011
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet:focus {
58768
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet:focus {
58012
58769
  outline: none;
58013
58770
  }
58014
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet:focus::before {
58771
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet:focus::before {
58015
58772
  background-color: var(--cc-primary-color-focus);
58016
58773
  box-shadow: 0 0 0 4px var(--cc-primary-color-opacity-10);
58017
58774
  }
58018
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet:focus-visible::before {
58775
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet:focus-visible::before {
58019
58776
  outline: 2px solid var(--cc-primary-color-focus);
58020
58777
  outline-offset: 2px;
58021
58778
  }
58022
- article .swiper._wrapper_fa7bu_117 button.swiper-pagination-bullet {
58779
+ article .swiper._wrapper_jw04w_138 button.swiper-pagination-bullet {
58023
58780
  border: none;
58024
58781
  margin: 0;
58025
58782
  padding: 0;
@@ -58027,17 +58784,17 @@ article .swiper._wrapper_fa7bu_117 button.swiper-pagination-bullet {
58027
58784
  -webkit-appearance: none;
58028
58785
  appearance: none;
58029
58786
  }
58030
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-clickable .swiper-pagination-bullet {
58787
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-clickable .swiper-pagination-bullet {
58031
58788
  cursor: pointer;
58032
58789
  }
58033
- article .swiper._wrapper_fa7bu_117 .swiper-pagination-bullet:only-child {
58790
+ article .swiper._wrapper_jw04w_138 .swiper-pagination-bullet:only-child {
58034
58791
  display: none !important;
58035
58792
  }
58036
58793
  article
58037
- .swiper._wrapper_fa7bu_117
58794
+ .swiper._wrapper_jw04w_138
58038
58795
  .swiper-horizontal > .swiper-pagination-bullets .swiper-pagination-bullet,
58039
58796
  article
58040
- .swiper._wrapper_fa7bu_117
58797
+ .swiper._wrapper_jw04w_138
58041
58798
  .swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet {
58042
58799
  /* Adjacent 24px targets touch but never overlap (WCAG 2.5.8). */
58043
58800
  margin: 0;
@@ -58953,8 +59710,8 @@ article
58953
59710
  margin-top: 10px;
58954
59711
  max-width: 295px;
58955
59712
  }
58956
- ._ac-pushbutton_hct50_1,
58957
- ._ac-pushbutton_hct50_1._subdued_hct50_2 {
59713
+ ._ac-pushbutton_1yco4_1,
59714
+ ._ac-pushbutton_1yco4_1._subdued_1yco4_2 {
58958
59715
  background-color: transparent;
58959
59716
  border: 1px solid primaryColor;
58960
59717
  color: primaryColor;
@@ -58966,7 +59723,7 @@ article
58966
59723
  border-radius: 14px;
58967
59724
  }
58968
59725
 
58969
- ._ac-pushbutton_hct50_1._style-default_hct50_14._expandable_hct50_14._expanded_hct50_14 {
59726
+ ._ac-pushbutton_1yco4_1._style-default_1yco4_14._expandable_1yco4_14._expanded_1yco4_14 {
58970
59727
  background: primaryGradient;
58971
59728
  color: primaryContrastColor;
58972
59729
  border: 1px solid primaryColor;
@@ -58984,19 +59741,19 @@ article
58984
59741
  padding: \${theme.unitSize}px \${theme.unitSize * 2}px;
58985
59742
  }*/
58986
59743
 
58987
- ._adaptivecard-wrapper_hct50_32 {
59744
+ ._adaptivecard-wrapper_1yco4_32 {
58988
59745
  width: 100%;
58989
59746
  box-sizing: border-box;
58990
59747
  max-width: 100%;
58991
59748
  padding: 16px !important;
58992
59749
  }
58993
59750
 
58994
- ._adaptivecard-wrapper_hct50_32 > *:focus {
59751
+ ._adaptivecard-wrapper_1yco4_32 > *:focus {
58995
59752
  outline: 1px solid var(--cc-primary-color-focus);
58996
59753
  border-color: var(--cc-primary-color-focus);
58997
59754
  }
58998
59755
 
58999
- ._adaptivecard-wrapper_hct50_32 button {
59756
+ ._adaptivecard-wrapper_1yco4_32 button {
59000
59757
  appearance: none;
59001
59758
  background: var(--cc-primary-button-background, var(--cc-black-10));
59002
59759
  border-radius: var(--cc-primary-button-border-radius, 10px);
@@ -59011,42 +59768,42 @@ article
59011
59768
  width: 100%;
59012
59769
  }
59013
59770
 
59014
- ._adaptivecard-wrapper_hct50_32 button:hover {
59771
+ ._adaptivecard-wrapper_1yco4_32 button:hover {
59015
59772
  background: var(--cc-primary-button-background-hover, var(--cc-black-30));
59016
59773
  }
59017
59774
 
59018
- ._adaptivecard-wrapper_hct50_32 button:focus-visible {
59775
+ ._adaptivecard-wrapper_1yco4_32 button:focus-visible {
59019
59776
  outline: 2px solid var(--cc-primary-color-focus);
59020
59777
  outline-offset: 2px;
59021
59778
  }
59022
59779
 
59023
- ._adaptivecard-wrapper_hct50_32._chatDisabled_hct50_68 button,
59024
- ._adaptivecard-wrapper_hct50_32._chatDisabled_hct50_68 button:hover {
59780
+ ._adaptivecard-wrapper_1yco4_32._chatDisabled_1yco4_68 button,
59781
+ ._adaptivecard-wrapper_1yco4_32._chatDisabled_1yco4_68 button:hover {
59025
59782
  background: var(--cc-primary-button-disabled-background, var(--cc-black-90));
59026
59783
  opacity: var(--cc-primary-button-disabled-opacity, 1);
59027
59784
  cursor: var(--cc-primary-button-disabled-cursor, default);
59028
59785
  pointer-events: none;
59029
59786
  }
59030
59787
 
59031
- ._adaptivecard-wrapper_hct50_32 button:disabled {
59788
+ ._adaptivecard-wrapper_1yco4_32 button:disabled {
59032
59789
  background: var(--cc-primary-button-disabled-background, var(--cc-black-90));
59033
59790
  opacity: var(--cc-primary-button-disabled-opacity, 1);
59034
59791
  cursor: var(--cc-primary-button-disabled-cursor, default);
59035
59792
  }
59036
59793
 
59037
- ._adaptivecard-wrapper_hct50_32 .ac-textRun {
59794
+ ._adaptivecard-wrapper_1yco4_32 .ac-textRun {
59038
59795
  color: var(--cc-black-40) !important;
59039
59796
  font-size: 12px !important;
59040
59797
  line-height: 140%;
59041
59798
  }
59042
59799
 
59043
- ._adaptivecard-wrapper_hct50_32 .ac-container {
59800
+ ._adaptivecard-wrapper_1yco4_32 .ac-container {
59044
59801
  border-radius: 15px;
59045
59802
  }
59046
59803
 
59047
- ._adaptivecard-wrapper_hct50_32 input,
59048
- ._adaptivecard-wrapper_hct50_32 textarea,
59049
- ._adaptivecard-wrapper_hct50_32 select {
59804
+ ._adaptivecard-wrapper_1yco4_32 input,
59805
+ ._adaptivecard-wrapper_1yco4_32 textarea,
59806
+ ._adaptivecard-wrapper_1yco4_32 select {
59050
59807
  align-self: stretch;
59051
59808
  background-position: right 12px center;
59052
59809
  background-repeat: no-repeat;
@@ -59062,14 +59819,14 @@ article
59062
59819
  }
59063
59820
 
59064
59821
  @media screen and (max-width: 575px) {
59065
- ._adaptivecard-wrapper_hct50_32 input,
59066
- ._adaptivecard-wrapper_hct50_32 textarea,
59067
- ._adaptivecard-wrapper_hct50_32 select {
59822
+ ._adaptivecard-wrapper_1yco4_32 input,
59823
+ ._adaptivecard-wrapper_1yco4_32 textarea,
59824
+ ._adaptivecard-wrapper_1yco4_32 select {
59068
59825
  font-size: 16px !important;
59069
59826
  }
59070
59827
  }
59071
59828
 
59072
- ._adaptivecard-wrapper_hct50_32 textarea {
59829
+ ._adaptivecard-wrapper_1yco4_32 textarea {
59073
59830
  min-height: 98px;
59074
59831
  }
59075
59832
 
@@ -59077,41 +59834,41 @@ article
59077
59834
  height: 16px !important;
59078
59835
  }
59079
59836
 
59080
- ._adaptivecard-wrapper_hct50_32 textarea::-webkit-scrollbar {
59837
+ ._adaptivecard-wrapper_1yco4_32 textarea::-webkit-scrollbar {
59081
59838
  width: 27px;
59082
59839
  }
59083
59840
 
59084
- ._adaptivecard-wrapper_hct50_32 textarea::-webkit-scrollbar-track {
59841
+ ._adaptivecard-wrapper_1yco4_32 textarea::-webkit-scrollbar-track {
59085
59842
  background: transparent;
59086
59843
  }
59087
59844
 
59088
- ._adaptivecard-wrapper_hct50_32 textarea::-webkit-scrollbar-thumb {
59845
+ ._adaptivecard-wrapper_1yco4_32 textarea::-webkit-scrollbar-thumb {
59089
59846
  background-color: var(--cc-black-80);
59090
59847
  border-radius: 10px;
59091
59848
  border: 12px solid var(--cc-white);
59092
59849
  border-block: none;
59093
59850
  }
59094
59851
 
59095
- ._adaptivecard-wrapper_hct50_32 input:focus,
59096
- ._adaptivecard-wrapper_hct50_32 textarea:focus,
59097
- ._adaptivecard-wrapper_hct50_32 select:focus {
59852
+ ._adaptivecard-wrapper_1yco4_32 input:focus,
59853
+ ._adaptivecard-wrapper_1yco4_32 textarea:focus,
59854
+ ._adaptivecard-wrapper_1yco4_32 select:focus {
59098
59855
  outline: 1px solid var(--cc-primary-color-focus);
59099
59856
  border-color: 1px solid var(--cc-primary-color-focus);
59100
59857
  }
59101
59858
 
59102
- ._adaptivecard-wrapper_hct50_32 input:focus-visible,
59103
- ._adaptivecard-wrapper_hct50_32 textarea:focus-visible,
59104
- ._adaptivecard-wrapper_hct50_32 select:focus-visible {
59859
+ ._adaptivecard-wrapper_1yco4_32 input:focus-visible,
59860
+ ._adaptivecard-wrapper_1yco4_32 textarea:focus-visible,
59861
+ ._adaptivecard-wrapper_1yco4_32 select:focus-visible {
59105
59862
  outline: 2px solid var(--cc-primary-color-focus);
59106
59863
  border-color: 1px solid var(--cc-primary-color-focus);
59107
59864
  }
59108
59865
 
59109
- ._adaptivecard-wrapper_hct50_32 .ac-actionSet {
59866
+ ._adaptivecard-wrapper_1yco4_32 .ac-actionSet {
59110
59867
  flex-wrap: wrap;
59111
59868
  gap: 8px;
59112
59869
  }
59113
59870
 
59114
- ._adaptivecard-wrapper_hct50_32 > * {
59871
+ ._adaptivecard-wrapper_1yco4_32 > * {
59115
59872
  background-color: white;
59116
59873
  border-radius: var(--cc-adaptivecard-inner-border-radius, var(--cc-bubble-border-radius, 15px));
59117
59874
  box-shadow: none;
@@ -59120,7 +59877,7 @@ article
59120
59877
  margin: -17px;
59121
59878
  }
59122
59879
 
59123
- ._adaptivecard-wrapper_hct50_32 [type="checkbox"] {
59880
+ ._adaptivecard-wrapper_1yco4_32 [type="checkbox"] {
59124
59881
  appearance: none;
59125
59882
  background-color: var(--cc-white);
59126
59883
  border: 1px solid var(--cc-black-10, #999);
@@ -59129,66 +59886,91 @@ article
59129
59886
  height: 16px;
59130
59887
  }
59131
59888
 
59132
- ._adaptivecard-wrapper_hct50_32 [type="checkbox"]:checked {
59889
+ ._adaptivecard-wrapper_1yco4_32 [type="checkbox"]:checked {
59133
59890
  background-color: var(--cc-black-10);
59134
59891
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12.5172 4.4569C12.8172 4.74256 12.8288 5.2173 12.5431 5.51724L7.19089 11.1371C6.69828 11.6543 5.87315 11.6543 5.38054 11.1371L3.4569 9.11724C3.17123 8.8173 3.18281 8.34256 3.48276 8.0569C3.78271 7.77123 4.25744 7.78281 4.5431 8.08276L6.28572 9.9125L11.4569 4.48276C11.7426 4.18281 12.2173 4.17123 12.5172 4.4569Z' fill='white'/%3E%3C/svg%3E");
59135
59892
  background-position: -1px -1px;
59136
59893
  }
59137
59894
 
59138
- ._adaptivecard-wrapper_hct50_32 [type="checkbox"]:active {
59895
+ ._adaptivecard-wrapper_1yco4_32 [type="checkbox"]:active {
59139
59896
  background-color: var(--cc-black-60);
59140
59897
  border-color: var(--cc-black-60);
59141
59898
  }
59142
59899
 
59143
- ._adaptivecard-wrapper_hct50_32 [type="checkbox"]:checked:hover {
59900
+ ._adaptivecard-wrapper_1yco4_32 [type="checkbox"]:checked:hover {
59144
59901
  background-color: var(--cc-black-40);
59145
59902
  border-color: var(--cc-black-40);
59146
59903
  }
59147
59904
 
59148
- ._adaptivecard-wrapper_hct50_32 input:invalid {
59905
+ ._adaptivecard-wrapper_1yco4_32 input:invalid {
59149
59906
  border-color: red;
59150
59907
  }
59151
59908
 
59152
- ._adaptivecard-wrapper_hct50_32 [type="date"]::-webkit-calendar-picker-indicator,
59153
- ._adaptivecard-wrapper_hct50_32 [type="time"]::-webkit-calendar-picker-indicator {
59909
+ /* Chromium's native picker button is kept (it is a keyboard stop of its own after the
59910
+ * mm/dd/yyyy segments) but its glyph is replaced by our SVG painted on the input at
59911
+ * \`right 12px center\`. Centre the button on that icon (16px content + 4px padding = a 24px
59912
+ * target, 2.5.8) so its focus ring wraps the icon instead of cutting across it (CGY-39786;
59913
+ * 2.4.13 best practice).
59914
+ *
59915
+ * Chromium ignores :focus/:focus-visible on this pseudo-element, and the host input matches
59916
+ * :focus only while a date/time segment has focus but :focus-within in both states, so
59917
+ * \`:focus-within:not(:focus)\` is exactly "the picker button has focus". Firefox has no such
59918
+ * pseudo-element or extra tab stop, so these rules are inert there. */
59919
+ ._adaptivecard-wrapper_1yco4_32 [type="date"]::-webkit-calendar-picker-indicator,
59920
+ ._adaptivecard-wrapper_1yco4_32 [type="time"]::-webkit-calendar-picker-indicator {
59154
59921
  background: none;
59922
+ box-sizing: content-box;
59923
+ width: 16px;
59924
+ height: 16px;
59925
+ padding: 4px;
59926
+ /* Pull the box 4px past the input's 12px right padding so it is centred on the icon. */
59927
+ margin: 0 -4px 0 0;
59928
+ align-self: center;
59929
+ border-radius: 4px;
59930
+ cursor: pointer;
59155
59931
  }
59156
59932
 
59157
- ._adaptivecard-wrapper_hct50_32 [type="date"],
59158
- ._adaptivecard-wrapper_hct50_32 [type="time"] {
59933
+ ._adaptivecard-wrapper_1yco4_32 [type="date"]:focus-within:not(:focus)::-webkit-calendar-picker-indicator,
59934
+ ._adaptivecard-wrapper_1yco4_32 [type="time"]:focus-within:not(:focus)::-webkit-calendar-picker-indicator {
59935
+ outline: 2px solid var(--cc-primary-color-focus);
59936
+ outline-offset: 0;
59937
+ }
59938
+
59939
+ ._adaptivecard-wrapper_1yco4_32 [type="date"],
59940
+ ._adaptivecard-wrapper_1yco4_32 [type="time"] {
59159
59941
  background-repeat: no-repeat;
59160
59942
  background-position: right 12px center;
59161
59943
  /* accent-color tints picker popup only; Firefox native indicator glyph is system-colored and unstyleable. */
59162
59944
  accent-color: var(--cc-primary-color);
59163
59945
  }
59164
59946
 
59165
- ._adaptivecard-wrapper_hct50_32 [type="date"] {
59947
+ ._adaptivecard-wrapper_1yco4_32 [type="date"] {
59166
59948
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M3 0.75C3 0.335786 3.33579 0 3.75 0C4.16421 0 4.5 0.335786 4.5 0.75V3H11V0.75C11 0.335786 11.3358 0 11.75 0C12.1642 0 12.5 0.335786 12.5 0.75V3H14C15.1046 3 16 3.89543 16 5V14C16 15.1046 15.1046 16 14 16H2C0.895431 16 0 15.1046 0 14V5C0 3.89543 0.895431 3 2 3H3V0.75ZM1.5 6H14.5V14C14.5 14.2761 14.2761 14.5 14 14.5H2C1.72386 14.5 1.5 14.2761 1.5 14V6Z' fill='%232455E6'/%3E%3C/svg%3E");
59167
59949
  }
59168
59950
 
59169
- ._adaptivecard-wrapper_hct50_32 [type="time"] {
59951
+ ._adaptivecard-wrapper_1yco4_32 [type="time"] {
59170
59952
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M8 14.5C11.5899 14.5 14.5 11.5899 14.5 8C14.5 4.41015 11.5899 1.5 8 1.5C4.41015 1.5 1.5 4.41015 1.5 8C1.5 11.5899 4.41015 14.5 8 14.5ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16Z' fill='%232455E6'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.0303 12.0303C10.7374 12.3232 10.2626 12.3232 9.96967 12.0303L7.25 9.31066L7.25 4C7.25 3.58579 7.58578 3.25 8 3.25C8.41421 3.25 8.75 3.58579 8.75 4L8.75 8.68934L11.0303 10.9697C11.3232 11.2626 11.3232 11.7374 11.0303 12.0303Z' fill='%232455E6'/%3E%3C/svg%3E");
59171
59953
  }
59172
59954
 
59173
59955
  /* Firefox stacks native date button beside background-image SVG (double-icon). Drop SVG; native icon remains, system-colored. */
59174
59956
  @supports (-moz-appearance: none) {
59175
- ._adaptivecard-wrapper_hct50_32 [type="date"] {
59957
+ ._adaptivecard-wrapper_1yco4_32 [type="date"] {
59176
59958
  background-image: none !important;
59177
59959
  }
59178
59960
  }
59179
59961
 
59180
- ._adaptivecard-wrapper_hct50_32 select {
59962
+ ._adaptivecard-wrapper_1yco4_32 select {
59181
59963
  appearance: none;
59182
59964
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M1.34923 4.24022C1.76855 3.8808 2.39985 3.92936 2.75927 4.34869L8.00002 10.4629L13.2408 4.34869C13.6002 3.92936 14.2315 3.8808 14.6508 4.24022C15.0701 4.59965 15.1187 5.23095 14.7593 5.65027L9.51853 11.7645C8.72034 12.6957 7.2797 12.6957 6.4815 11.7645L1.24076 5.65027C0.881339 5.23095 0.929901 4.59965 1.34923 4.24022Z' fill='%232455E6'/%3E%3C/svg%3E");
59183
59965
  background-repeat: no-repeat;
59184
59966
  background-position: right 12px center;
59185
59967
  }
59186
59968
 
59187
- ._adaptivecard-wrapper_hct50_32 select::-ms-expand {
59969
+ ._adaptivecard-wrapper_1yco4_32 select::-ms-expand {
59188
59970
  display: none;
59189
59971
  }
59190
59972
 
59191
- ._adaptivecard-wrapper_hct50_32 select option {
59973
+ ._adaptivecard-wrapper_1yco4_32 select option {
59192
59974
  color: var(--cc-black-40);
59193
59975
  border-bottom: 1px solid var(--cc-black-10);
59194
59976
  }
@@ -60204,6 +60986,9 @@ const getBackgroundImage = url => {
60204
60986
  sanitized = sanitized.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\)/g, "\\)");
60205
60987
  return `url("${sanitized}")`;
60206
60988
  };
60989
+ function resolveAttachmentUrl(attachment) {
60990
+ return attachment.downloadUrl ?? attachment.url;
60991
+ }
60207
60992
  const getRandomId = (prefix = "") => {
60208
60993
  const id = window?.crypto?.randomUUID?.() || Date.now();
60209
60994
  return prefix ? `${prefix}-${id}` : id;
@@ -60224,6 +61009,15 @@ const htmlToPlainText = html2 => {
60224
61009
  }
60225
61010
  return html2.replace(/<[^>]*>/g, "").trim();
60226
61011
  };
61012
+ const INTERACTIVE_CONTENT_SELECTOR = ["a[href]", "area[href]", "audio[controls]", "video[controls]", "button", "details", "embed", "iframe", "img[usemap]", "object[usemap]", 'input:not([type="hidden"])', "label", "select", "textarea", '[contenteditable]:not([contenteditable="false"])', "[tabindex]"].join(", ");
61013
+ const htmlHasInteractiveContent = html2 => {
61014
+ if (!html2) return false;
61015
+ if (typeof DOMParser !== "undefined") {
61016
+ const doc = new DOMParser().parseFromString(html2, "text/html");
61017
+ return !!doc.body?.querySelector(INTERACTIVE_CONTENT_SELECTOR);
61018
+ }
61019
+ return /<(a|area|audio|video|button|details|embed|iframe|img|object|input|label|select|textarea)[\s>]|\s(contenteditable|tabindex)=/i.test(html2);
61020
+ };
60227
61021
  const getFocusableElements = element2 => {
60228
61022
  const interactiveEls = element2?.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])');
60229
61023
  const interactiveElsArray = interactiveEls && Array.from(interactiveEls);
@@ -61529,6 +62323,7 @@ function createDOMPurify() {
61529
62323
  }
61530
62324
  var purify = createDOMPurify();
61531
62325
  const allowedHtmlTags = ["a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend", "li", "link", "main", "map", "mark", "meta", "meter", "nav", "noframes", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "small", "source", "span", "strike", "strong", "style", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"];
62326
+ const defaultAllowedHtmlTags = Object.freeze([...allowedHtmlTags]);
61532
62327
  const allowedHtmlAttributes = ["accept", "accept-charset", "accesskey", "action", "align", "alt", "autocomplete", "autofocus", "autoplay", "bgcolor", "border", "charset", "checked", "cite", "class", "color", "cols", "colspan", "content", "contenteditable", "controls", "coords", "data", "data-*", "datetime", "default", "dir", "dirname", "disabled", "download", "draggable", "dropzone", "enctype", "for", "form", "formaction", "headers", "height", "hidden", "high", "href", "hreflang", "http-equiv", "id", "ismap", "kind", "label", "lang", "list", "loop", "low", "max", "maxlength", "media", "method", "min", "multiple", "muted", "name", "novalidate", "open", "optimum", "pattern", "placeholder", "poster", "preload", "readonly", "rel", "required", "reversed", "rows", "rowspan", "sandbox", "scope", "selected", "shape", "size", "sizes", "span", "spellcheck", "src", "srcdoc", "srclang", "srcset", "start", "step", "style", "tabindex", "target", "title", "translate", "type", "usemap", "value", "width", "wrap"];
61533
62328
  const config$2 = {
61534
62329
  ALLOWED_TAGS: allowedHtmlTags,
@@ -85980,12 +86775,12 @@ const Lightbox = () => {
85980
86775
  }), /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(LightboxHeader, {}), /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {})]
85981
86776
  });
85982
86777
  };
85983
- const wrapper$5 = "_wrapper_2f2tp_1";
85984
- const fixedImage = "_fixedImage_2f2tp_9";
85985
- const flexImage = "_flexImage_2f2tp_10";
85986
- const wrapperDownloadable = "_wrapperDownloadable_2f2tp_26";
85987
- const downloadButtonWrapper$1 = "_downloadButtonWrapper_2f2tp_51";
85988
- const brokenImage$1 = "_brokenImage_2f2tp_55";
86778
+ const wrapper$5 = "_wrapper_tochy_1";
86779
+ const fixedImage = "_fixedImage_tochy_9";
86780
+ const flexImage = "_flexImage_tochy_10";
86781
+ const wrapperDownloadable = "_wrapperDownloadable_tochy_31";
86782
+ const downloadButtonWrapper$1 = "_downloadButtonWrapper_tochy_56";
86783
+ const brokenImage$1 = "_brokenImage_tochy_60";
85989
86784
  const classes$g = {
85990
86785
  wrapper: wrapper$5,
85991
86786
  fixedImage,
@@ -86611,8 +87406,13 @@ const Image = () => {
86611
87406
  const {
86612
87407
  url,
86613
87408
  altText,
86614
- buttons: buttons2
87409
+ buttons: buttons2,
87410
+ downloadUrl
86615
87411
  } = payload?.message?.attachment?.payload || {};
87412
+ const resolvedUrl = resolveAttachmentUrl({
87413
+ url,
87414
+ downloadUrl
87415
+ });
86616
87416
  const button2 = buttons2?.[0];
86617
87417
  const buttonRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
86618
87418
  const isDownloadable = buttons2?.find(button22 => "type" in button22 && button22.type === "web_url") !== void 0;
@@ -86623,12 +87423,12 @@ const Image = () => {
86623
87423
  setShowLightbox(false);
86624
87424
  buttonRef.current?.focus();
86625
87425
  },
86626
- url,
87426
+ url: resolvedUrl ?? "",
86627
87427
  altText,
86628
87428
  isDownloadable,
86629
87429
  button: button2
86630
- }), [altText, button2, isDownloadable, url]);
86631
- if (!url) return null;
87430
+ }), [altText, button2, isDownloadable, resolvedUrl]);
87431
+ if (!resolvedUrl) return null;
86632
87432
  return /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(ImageMessageContext.Provider, {
86633
87433
  value: contextValue,
86634
87434
  children: [/* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(ImageThumb, {
@@ -88236,8 +89036,13 @@ const Video = () => {
88236
89036
  const {
88237
89037
  url,
88238
89038
  altText,
88239
- captionsUrl
89039
+ captionsUrl,
89040
+ downloadUrl
88240
89041
  } = payload?.message?.attachment?.payload || {};
89042
+ const resolvedUrl = resolveAttachmentUrl({
89043
+ url,
89044
+ downloadUrl
89045
+ });
88241
89046
  const downloadTranscriptLinkRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
88242
89047
  const [playing, setPlaying] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
88243
89048
  const videoPlayerWrapperRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
@@ -88248,7 +89053,7 @@ const Video = () => {
88248
89053
  hasTranscript: !!altText,
88249
89054
  hasCaptions: !!captionsUrl
88250
89055
  },
88251
- validation: () => !!url
89056
+ validation: () => !!resolvedUrl
88252
89057
  });
88253
89058
  const handleFocus = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(player2 => {
88254
89059
  const chatHistory = document.getElementById("webchatChatHistoryWrapperLiveLogPanel");
@@ -88288,7 +89093,7 @@ const Video = () => {
88288
89093
  label: "On"
88289
89094
  }]
88290
89095
  };
88291
- if (!url) return null;
89096
+ if (!resolvedUrl) return null;
88292
89097
  const playVideoLabel = config2?.settings.customTranslations?.ariaLabels?.playVideo || "Play video";
88293
89098
  return (
88294
89099
  // Latest version of ReactPlayer at the moment supports previewTabIndex and previewArialLabel props for passing tabindex and aria-label to the video preview button (light mode),
@@ -88307,7 +89112,7 @@ const Video = () => {
88307
89112
  onKeyDown: handleKeyDown,
88308
89113
  children: /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(ReactPlayer, {
88309
89114
  ref: videoPlayerRef,
88310
- url,
89115
+ url: resolvedUrl,
88311
89116
  light: lightMode,
88312
89117
  playing,
88313
89118
  controls: true,
@@ -93121,8 +93926,13 @@ const Audio = () => {
93121
93926
  const payload = getChannelPayload(message2, config2);
93122
93927
  const {
93123
93928
  url,
93124
- altText
93929
+ altText,
93930
+ downloadUrl
93125
93931
  } = payload?.message?.attachment?.payload || {};
93932
+ const resolvedUrl = resolveAttachmentUrl({
93933
+ url,
93934
+ downloadUrl
93935
+ });
93126
93936
  const playerRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
93127
93937
  const [playing, setPlaying] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
93128
93938
  const [progress, setProgress] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(0);
@@ -93155,14 +93965,14 @@ const Audio = () => {
93155
93965
  data: {
93156
93966
  hasTranscript: !!altText
93157
93967
  },
93158
- validation: () => !!url
93968
+ validation: () => !!resolvedUrl
93159
93969
  });
93160
- if (!url) return null;
93970
+ if (!resolvedUrl) return null;
93161
93971
  return /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
93162
93972
  className: classnames$1("webchat-media-template-audio", classes$b.wrapper),
93163
93973
  "data-testid": "audio-message",
93164
93974
  children: [/* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(ReactPlayer, {
93165
- url,
93975
+ url: resolvedUrl,
93166
93976
  onReady: handleFocus,
93167
93977
  ref: playerRef,
93168
93978
  playing,
@@ -93249,16 +94059,21 @@ const File = props2 => {
93249
94059
  const {
93250
94060
  fileName: fileName2,
93251
94061
  size: size2,
93252
- url
94062
+ url,
94063
+ downloadUrl
93253
94064
  } = attachment;
94065
+ const resolvedUrl = resolveAttachmentUrl({
94066
+ url,
94067
+ downloadUrl
94068
+ });
93254
94069
  return /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("a", {
93255
- href: url,
94070
+ href: resolvedUrl,
93256
94071
  target: "_blank",
93257
94072
  style: {
93258
94073
  textDecoration: "none"
93259
94074
  },
93260
94075
  children: /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("img", {
93261
- src: url,
94076
+ src: resolvedUrl,
93262
94077
  alt: `${fileName2} (${getSizeLabel(size2)})`,
93263
94078
  className: classnames$1(attachments.length > 1 ? classes$a.smallImagePrevew : classes$a.imagePreview, "webchat-media-template-image"),
93264
94079
  "data-testid": "image-message",
@@ -93272,10 +94087,15 @@ const File = props2 => {
93272
94087
  const {
93273
94088
  fileName: fileName2,
93274
94089
  size: size2,
93275
- url
94090
+ url,
94091
+ downloadUrl
93276
94092
  } = attachment;
94093
+ const resolvedUrl = resolveAttachmentUrl({
94094
+ url,
94095
+ downloadUrl
94096
+ });
93277
94097
  return /* @__PURE__ */(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("a", {
93278
- href: url,
94098
+ href: resolvedUrl,
93279
94099
  target: "_blank",
93280
94100
  style: {
93281
94101
  textDecoration: "none"
@@ -93582,14 +94402,14 @@ const List = () => {
93582
94402
  })]
93583
94403
  });
93584
94404
  };
93585
- const slideItem = "_slideItem_fa7bu_1";
93586
- const top = "_top_fa7bu_8";
93587
- const bottom = "_bottom_fa7bu_13";
93588
- const brokenImage = "_brokenImage_fa7bu_34";
93589
- const hasExtraInfo = "_hasExtraInfo_fa7bu_43";
93590
- const buttonListItem = "_buttonListItem_fa7bu_82";
93591
- const link = "_link_fa7bu_91";
93592
- const wrapper$1 = "_wrapper_fa7bu_117";
94405
+ const slideItem = "_slideItem_jw04w_1";
94406
+ const top = "_top_jw04w_8";
94407
+ const bottom = "_bottom_jw04w_13";
94408
+ const brokenImage = "_brokenImage_jw04w_34";
94409
+ const hasExtraInfo = "_hasExtraInfo_jw04w_43";
94410
+ const buttonListItem = "_buttonListItem_jw04w_82";
94411
+ const link = "_link_jw04w_91";
94412
+ const wrapper$1 = "_wrapper_jw04w_138";
93593
94413
  const classes$8 = {
93594
94414
  slideItem,
93595
94415
  top,
@@ -99375,7 +100195,6 @@ const getHostname = url => {
99375
100195
  return "";
99376
100196
  }
99377
100197
  };
99378
- const NESTED_CONTROL_SELECTOR = "a[href], button, input, select, textarea, summary";
99379
100198
  const GalleryItem = props2 => {
99380
100199
  const {
99381
100200
  slide: slide2,
@@ -99415,7 +100234,8 @@ const GalleryItem = props2 => {
99415
100234
  const linkUrl = default_action?.url ? config2?.settings?.layout?.disableUrlButtonSanitization ? default_action.url : distExports.sanitizeUrl(default_action.url) : "";
99416
100235
  const handleClick = event => {
99417
100236
  const target = event.target;
99418
- if (target !== event.currentTarget && target.closest(NESTED_CONTROL_SELECTOR)) return;
100237
+ const control = target.closest(INTERACTIVE_CONTENT_SELECTOR);
100238
+ if (control && control !== event.currentTarget) return;
99419
100239
  if (!linkUrl || linkUrl === "about:blank") return;
99420
100240
  window.open(linkUrl);
99421
100241
  };
@@ -99441,7 +100261,8 @@ const GalleryItem = props2 => {
99441
100261
  id: subtitleId,
99442
100262
  className: "webchat-carousel-template-subtitle"
99443
100263
  });
99444
- const hasBlockText = hasSubtitle || titleBelowImage && hasTitle;
100264
+ const blockTextHasControls = htmlHasInteractiveContent(subtitleHtml) || titleBelowImage && htmlHasInteractiveContent(titleHtml);
100265
+ const hasBlockText = (hasSubtitle || titleBelowImage && hasTitle) && !blockTextHasControls;
99445
100266
  const linkTarget = linkUrl && linkUrl !== "about:blank" ? hasBlockText ? "text" : "top" : null;
99446
100267
  const titleText = linkTarget ? htmlToPlainText(titleHtml) : "";
99447
100268
  const subtitleText = linkTarget ? htmlToPlainText(subtitleHtml) : "";
@@ -118721,10 +119542,13 @@ function customElements(pluginConfig) {
118721
119542
  function setTimeAlly() {
118722
119543
  fp?.calendarContainer?.setAttribute("aria-labelledby", "webchatDatePickerHeaderLabel");
118723
119544
  if (fp?.config?.enableTime) {
119545
+ const labels = customTranslations?.ariaLabels;
118724
119546
  const hourField = fp?.timeContainer?.getElementsByClassName("flatpickr-hour")?.[0];
118725
119547
  hourField?.setAttribute("tabIndex", "0");
119548
+ if (labels?.datePickerHour) hourField?.setAttribute("aria-label", labels.datePickerHour);
118726
119549
  const minutesField = fp?.timeContainer?.getElementsByClassName("flatpickr-minute")?.[0];
118727
119550
  minutesField?.setAttribute("tabIndex", "0");
119551
+ if (labels?.datePickerMinute) minutesField?.setAttribute("aria-label", labels.datePickerMinute);
118728
119552
  const amPmField = fp?.timeContainer?.getElementsByClassName("flatpickr-am-pm")?.[0];
118729
119553
  amPmField?.setAttribute("tabIndex", "0");
118730
119554
  }
@@ -118815,6 +119639,9 @@ function customElements(pluginConfig) {
118815
119639
  if (cell.classList.contains("endRange")) {
118816
119640
  parts.push(labels?.datePickerRangeEnd || "end of range");
118817
119641
  }
119642
+ if (isSelectedCell(cell)) {
119643
+ parts.push(labels?.datePickerSelected || "selected");
119644
+ }
118818
119645
  return parts.join(", ");
118819
119646
  }
118820
119647
  function sameYMD(a, b) {
@@ -134993,6 +135820,12 @@ const AdaptiveCard = props2 => {
134993
135820
  headings.forEach(heading2 => {
134994
135821
  if (heading2.getAttribute("aria-level") === null) heading2.setAttribute("aria-level", "4");
134995
135822
  });
135823
+ const placeholderOptions = targetRef.current.querySelectorAll("select.ac-choiceSetInput-compact > option[disabled][hidden][value='']");
135824
+ placeholderOptions.forEach(option => {
135825
+ if (!option.text) return;
135826
+ option.disabled = false;
135827
+ option.hidden = false;
135828
+ });
134996
135829
  const container = targetRef.current.querySelector(".ac-container.ac-adaptiveCard[aria-label][tabindex='0']");
134997
135830
  if (container) {
134998
135831
  const ariaLabel = container.getAttribute("aria-label");
@@ -135016,9 +135849,9 @@ const AdaptiveCard = props2 => {
135016
135849
  ref: targetRef
135017
135850
  });
135018
135851
  };
135019
- const chatDisabled = "_chatDisabled_hct50_68";
135852
+ const chatDisabled = "_chatDisabled_1yco4_68";
135020
135853
  const styles = {
135021
- "adaptivecard-wrapper": "_adaptivecard-wrapper_hct50_32",
135854
+ "adaptivecard-wrapper": "_adaptivecard-wrapper_1yco4_32",
135022
135855
  chatDisabled
135023
135856
  };
135024
135857
  const adaptiveCardsHostConfig = {