@cognigy/webchat 3.43.0 → 3.44.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.
package/OSS_LICENSES.txt CHANGED
@@ -1,4 +1,4 @@
1
- Created on 15-05-2026 at 12:21:32
1
+ Created on 16-06-2026 at 13:58:08
2
2
 
3
3
  {
4
4
  "@babel/code-frame@7.29.0": {
@@ -170,7 +170,7 @@ Created on 15-05-2026 at 12:21:32
170
170
  "licenseFile": "node_modules/@cognigy/socket-client/LICENSE",
171
171
  "copyright": "Copyright (c) 2019 Cognigy GmbH"
172
172
  },
173
- "@cognigy/webchat@3.43.0": {
173
+ "@cognigy/webchat@3.44.0": {
174
174
  "licenses": "MIT*",
175
175
  "repository": "https://github.com/Cognigy/Webchat",
176
176
  "publisher": "Cognigy GmbH",
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * webchat.esm.js v3.43.0
3
- * https://github.com/Cognigy/Webchat/tree/v3.43.0
4
- * https://github.com/Cognigy/Webchat/tree/v3.43.0/OSS_LICENSES.txt
2
+ * webchat.esm.js v3.44.0
3
+ * https://github.com/Cognigy/Webchat/tree/v3.44.0
4
+ * https://github.com/Cognigy/Webchat/tree/v3.44.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";
@@ -38178,6 +38178,23 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38178
38178
  this.props.onShowChatScreen();
38179
38179
  }
38180
38180
  };
38181
+
38182
+ /**
38183
+ * Emits the `webchat/close-button` analytics event.
38184
+ *
38185
+ * Fired only from the close ("X") button handlers — the chat window header
38186
+ * (`handleCloseAndReset`) and the disconnect overlay (`handleClose`). It is
38187
+ * NOT emitted for the toggle button, the home-screen close (which minimizes),
38188
+ * or the programmatic `webchat.close()` / `webchat.toggle()` APIs.
38189
+ *
38190
+ * This lives in the component rather than the redux analytics middleware
38191
+ * (where `webchat/open|close|minimize` are emitted) on purpose: the store only
38192
+ * sees the resulting `SET_OPEN` action and cannot tell which control triggered
38193
+ * the close, so the originating interaction is only known here.
38194
+ */
38195
+ emitCloseButtonAnalytics = () => {
38196
+ this.props.onEmitAnalytics("webchat/close-button");
38197
+ };
38181
38198
  render() {
38182
38199
  const {
38183
38200
  props,
@@ -38242,7 +38259,8 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38242
38259
  } = state;
38243
38260
  const {
38244
38261
  widgetSettings: {
38245
- disableToggleButton
38262
+ disableToggleButton,
38263
+ disableMobileScrollLock
38246
38264
  },
38247
38265
  behavior: {
38248
38266
  enableConnectionStatusIndicator
@@ -38285,12 +38303,15 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38285
38303
  const chatRegionAriaLabel = config.settings.customTranslations?.ariaLabels?.chatRegion ?? "Chat window";
38286
38304
  const handleClose = () => {
38287
38305
  onClose();
38306
+ // The disconnect overlay's close ("X") is also a header close button, so
38307
+ // it emits "webchat/close-button" for parity with the chat window header.
38308
+ this.emitCloseButtonAnalytics();
38288
38309
  this.chatToggleButtonRef.current?.focus();
38289
38310
  };
38290
38311
  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(_emotion_react__WEBPACK_IMPORTED_MODULE_2__.a, {
38291
38312
  theme: theme
38292
38313
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react__WEBPACK_IMPORTED_MODULE_0__["default"].Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(react_remove_scroll__WEBPACK_IMPORTED_MODULE_14__/* ["default"] */ .A, {
38293
- enabled: open && isMobile,
38314
+ enabled: open && isMobile && !disableMobileScrollLock,
38294
38315
  allowPinchZoom: true
38295
38316
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(_presentational_WebchatWrapper__WEBPACK_IMPORTED_MODULE_17__/* ["default"] */ .A, _extends({
38296
38317
  "data-cognigy-webchat-root": true
@@ -38419,6 +38440,10 @@ class WebchatUI extends react__WEBPACK_IMPORTED_MODULE_0__["default"].PureCompon
38419
38440
  const handleCloseAndReset = () => {
38420
38441
  onSetShowHomeScreen(true);
38421
38442
  onClose();
38443
+ // `onClose()` emits the generic "webchat/close" (which also fires for the
38444
+ // toggle button); this additionally emits "webchat/close-button" so
38445
+ // integrations can react to an explicit header close only.
38446
+ this.emitCloseButtonAnalytics();
38422
38447
  // Restore focus to chat toggle button
38423
38448
  this.chatToggleButtonRef?.current?.focus?.();
38424
38449
  };
@@ -42142,29 +42167,51 @@ const ScreenReaderLiveRegion = ({
42142
42167
  return message !== null && typeof message === "object" && "animationState" in message;
42143
42168
  };
42144
42169
  const timeout = setTimeout(() => {
42145
- const firstUnannouncedMsg = unannouncedMessages[0]; // Only announce one at a time
42146
- const id = `webchatMessageId-${firstUnannouncedMsg.timestamp}`;
42170
+ // Scan for the first unannounced message that is currently announceable.
42171
+ // We must NOT stop at index 0 for non-rendered (data-only) messages,
42172
+ // otherwise a permanently data-only message would block all later ones.
42173
+ for (const candidate of unannouncedMessages) {
42174
+ const id = `webchatMessageId-${candidate.timestamp}`;
42175
+
42176
+ // A streaming message that hasn't finished blocks later announcements
42177
+ // (preserves in-order announcement of streamed output).
42178
+ const isStreaming = isProgressiveRenderingEnabled && isStreamingMessage(candidate) && (candidate.animationState === "start" || candidate.animationState === "animating");
42179
+ if (isStreaming) return;
42180
+
42181
+ // Event status pills announce themselves via aria-live="assertive".
42182
+ // Mark handled and continue; they must not block later real messages.
42183
+ if (liveContent[id] === `IGNORE-${id}`) {
42184
+ announcedIdsRef.current.add(id);
42185
+ continue;
42186
+ }
42187
+
42188
+ // Prefer chat-components' live content; otherwise read the DOM.
42189
+ // getTextFromDOM returns null when no <article data-message-id>
42190
+ // node exists, which is the signal for a not-rendered message.
42191
+ const liveText = liveContent[id];
42192
+ const domText = liveText ? null : (0,_utils_live_region_announcement__WEBPACK_IMPORTED_MODULE_2__/* .getTextFromDOM */ .g9)(id);
42147
42193
 
42148
- // Check if this is a streaming message that hasn't finished
42149
- const isStreaming = isProgressiveRenderingEnabled && isStreamingMessage(firstUnannouncedMsg) && (firstUnannouncedMsg.animationState === "start" || firstUnannouncedMsg.animationState === "animating");
42194
+ // "Rendered" iff chat-components produced live content for it,
42195
+ // or an <article data-message-id> node exists in the DOM.
42196
+ const isRendered = Boolean(liveText) || domText !== null;
42150
42197
 
42151
- // If streaming, don't announce yet
42152
- if (isStreaming) return;
42153
- announcedIdsRef.current.add(id);
42198
+ // Data-only / unsupported message: not in the chat log UI.
42199
+ // Skip WITHOUT marking announced so it can still be announced
42200
+ // if it becomes rendered later (e.g. progressive rendering).
42201
+ if (!isRendered) {
42202
+ continue;
42203
+ }
42154
42204
 
42155
- // Skip announcement if the message is marked as "IGNORE". Done by ChatEvent message component, as it has aria-live="assertive"
42156
- if (liveContent[id] === `IGNORE-${id}`) {
42157
- setLiveMessage(null);
42205
+ // Rendered: announce exactly one message per effect run.
42206
+ // Fall back to "A new message" for rendered-but-textless nodes.
42207
+ announcedIdsRef.current.add(id);
42208
+ const text = (0,_utils_live_region_announcement__WEBPACK_IMPORTED_MODULE_2__/* .cleanUpText */ .Yx)(liveText || domText || "A new message");
42209
+ setLiveMessage({
42210
+ id,
42211
+ text
42212
+ });
42158
42213
  return;
42159
42214
  }
42160
-
42161
- // Use live content if available, otherwise extract from DOM
42162
- const rawText = liveContent[id] || (0,_utils_live_region_announcement__WEBPACK_IMPORTED_MODULE_2__/* .getTextFromDOM */ .g9)(id);
42163
- const text = (0,_utils_live_region_announcement__WEBPACK_IMPORTED_MODULE_2__/* .cleanUpText */ .Yx)(rawText || "A new message");
42164
- setLiveMessage({
42165
- id,
42166
- text
42167
- });
42168
42215
  }, 100);
42169
42216
  return () => clearTimeout(timeout);
42170
42217
  }, [messages, liveContent, isProgressiveRenderingEnabled]);
@@ -44180,12 +44227,17 @@ const extractTextForScreenReader = root => {
44180
44227
  * - Querying the DOM for the element with the specified ID
44181
44228
  * - Using the extractTextForScreenReader function to get the text content
44182
44229
  *
44230
+ * The <article data-message-id="..."> node only exists when chat-components'
44231
+ * Message matcher matched at least one plugin. Data-only / unsupported
44232
+ * messages produce no DOM node, so callers can use a `null` return to tell a
44233
+ * not-rendered message apart from a rendered-but-textless one.
44234
+ *
44183
44235
  * @param id - The message ID to extract text for
44184
- * @returns The extracted text or a default message if not found
44236
+ * @returns The extracted text, or `null` if no element exists for the id
44185
44237
  */
44186
44238
  const getTextFromDOM = id => {
44187
44239
  const messageElement = document.querySelector(`[data-message-id="${id}"]`);
44188
- return messageElement ? extractTextForScreenReader(messageElement) : "A new message";
44240
+ return messageElement ? extractTextForScreenReader(messageElement) : null;
44189
44241
  };
44190
44242
 
44191
44243
  /***/ },
@@ -45597,6 +45649,7 @@ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
45597
45649
  disableTextInputSanitization: false,
45598
45650
  disableToggleButton: false,
45599
45651
  disableTeaserMarkdownRemoval: false,
45652
+ disableMobileScrollLock: false,
45600
45653
  enableAutoFocus: false,
45601
45654
  enableInjectionWithoutEmptyHistory: false,
45602
45655
  enableFocusTrap: false,
@@ -45892,10 +45945,23 @@ const createInputCollationMiddleware = () => {
45892
45945
  messages
45893
45946
  } = store.getState().inputCollation;
45894
45947
  const text = messages.map(message => message.text).join(" ");
45948
+
45949
+ /**
45950
+ * Preserve file upload attachments while collating. Each collatable input
45951
+ * carries its attachments in `data.attachments`, so we merge them across all
45952
+ * buffered messages. Without this, uploaded files are silently dropped when
45953
+ * input collation is enabled (no file payload reaches the flow).
45954
+ */
45955
+ const attachments = messages.flatMap(message => {
45956
+ const fileAttachments = message.data?.attachments;
45957
+ return Array.isArray(fileAttachments) ? fileAttachments : [];
45958
+ });
45895
45959
  const message = {
45896
45960
  ...messages[0],
45897
45961
  text,
45898
- data: undefined
45962
+ data: attachments.length > 0 ? {
45963
+ attachments
45964
+ } : undefined
45899
45965
  };
45900
45966
  return message;
45901
45967
  };