@eshal-bot/chat-widget 0.2.31 → 0.2.32

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 +1 @@
1
- export{S as default}from"./chat-widget-index-DGutMRlI.esm.js";import"react";import"react-dom/client";import"react/jsx-runtime";import"react-dom";
1
+ export{S as default}from"./chat-widget-index-kV_zpR6R.esm.js";import"react";import"react-dom/client";import"react/jsx-runtime";import"react-dom";
@@ -1177,7 +1177,7 @@
1177
1177
  function lb(a, b) {
1178
1178
  return null == a || "http://www.w3.org/1999/xhtml" === a ? kb(b) : "http://www.w3.org/2000/svg" === a && "foreignObject" === b ? "http://www.w3.org/1999/xhtml" : a;
1179
1179
  }
1180
- var mb,
1180
+ var mb$1,
1181
1181
  nb = function (a) {
1182
1182
  return "undefined" !== typeof MSApp && MSApp.execUnsafeLocalFunction ? function (b, c, d, e) {
1183
1183
  MSApp.execUnsafeLocalFunction(function () {
@@ -1186,9 +1186,9 @@
1186
1186
  } : a;
1187
1187
  }(function (a, b) {
1188
1188
  if ("http://www.w3.org/2000/svg" !== a.namespaceURI || "innerHTML" in a) a.innerHTML = b;else {
1189
- mb = mb || document.createElement("div");
1190
- mb.innerHTML = "<svg>" + b.valueOf().toString() + "</svg>";
1191
- for (b = mb.firstChild; a.firstChild;) a.removeChild(a.firstChild);
1189
+ mb$1 = mb$1 || document.createElement("div");
1190
+ mb$1.innerHTML = "<svg>" + b.valueOf().toString() + "</svg>";
1191
+ for (b = mb$1.firstChild; a.firstChild;) a.removeChild(a.firstChild);
1192
1192
  for (; b.firstChild;) a.appendChild(b.firstChild);
1193
1193
  }
1194
1194
  });
@@ -12080,11 +12080,43 @@
12080
12080
  }
12081
12081
  return fallbackIsRtl;
12082
12082
  }
12083
+
12084
+ // A file the chat will not take is answered with a message from the agent,
12085
+ // as the other channels do, not with a warning chip under the composer. The
12086
+ // same wording and languages as the channels' lines
12087
+ // (backend channels/services/inboundAttachmentRejection.ts); other locales
12088
+ // read English. {max_mb}, {used_mb} and {types} are filled at the call site.
12089
+ const ATTACHMENT_REJECTION_TEXTS = {
12090
+ en: {
12091
+ attachmentLimitReached: "That file is too large. This chat can receive up to {max_mb} MB of files in total.",
12092
+ attachmentLimitReachedUsed: "That file is too large. This chat can receive up to {max_mb} MB of files in total, and {used_mb} MB is already used.",
12093
+ attachmentFileTooLarge: "Sorry, that file is too large. Files must be under {max_mb} MB.",
12094
+ attachmentTypeNotAllowed: "Sorry, we can't accept that type of file. You can send {types}."
12095
+ },
12096
+ ar: {
12097
+ attachmentLimitReached: "حجم الملف كبير جداً. يمكن لهذه المحادثة استقبال ملفات يصل مجموع حجمها إلى {max_mb} ميغابايت.",
12098
+ attachmentLimitReachedUsed: "حجم الملف كبير جداً. يمكن لهذه المحادثة استقبال ملفات يصل مجموع حجمها إلى {max_mb} ميغابايت، وقد تم استخدام {used_mb} ميغابايت.",
12099
+ attachmentFileTooLarge: "عذراً، حجم الملف كبير جداً. يجب أن يكون حجم الملف أقل من {max_mb} ميغابايت.",
12100
+ attachmentTypeNotAllowed: "عذراً، لا يمكننا استقبال هذا النوع من الملفات. يمكنك إرسال: {types}."
12101
+ },
12102
+ zh: {
12103
+ attachmentLimitReached: "文件过大。此聊天最多可接收共 {max_mb} MB 的文件。",
12104
+ attachmentLimitReachedUsed: "文件过大。此聊天最多可接收共 {max_mb} MB 的文件,目前已使用 {used_mb} MB。",
12105
+ attachmentFileTooLarge: "抱歉,文件过大。文件大小必须小于 {max_mb} MB。",
12106
+ attachmentTypeNotAllowed: "抱歉,无法接收此类文件。您可以发送:{types}。"
12107
+ },
12108
+ ja: {
12109
+ attachmentLimitReached: "ファイルが大きすぎます。このチャットで受け取れるファイルは合計 {max_mb} MB までです。",
12110
+ attachmentLimitReachedUsed: "ファイルが大きすぎます。このチャットで受け取れるファイルは合計 {max_mb} MB までで、すでに {used_mb} MB を使用しています。",
12111
+ attachmentFileTooLarge: "申し訳ありませんが、ファイルが大きすぎます。ファイルは {max_mb} MB 未満にしてください。",
12112
+ attachmentTypeNotAllowed: "申し訳ありませんが、この種類のファイルは受け付けていません。送信できるファイル: {types}"
12113
+ }
12114
+ };
12083
12115
  function translate(locale, key) {
12084
12116
  const normalized = normalizeLocale(locale);
12085
12117
  const dict = DICTIONARIES[normalized] || EN;
12086
12118
  const onboarding = ONBOARDING_PLACEHOLDERS[normalized];
12087
- return onboarding?.[key] ?? dict[key] ?? A11Y_LABELS[normalized]?.[key] ?? ONBOARDING_PLACEHOLDERS.en[key] ?? A11Y_LABELS.en[key] ?? EN[key] ?? key;
12119
+ return onboarding?.[key] ?? dict[key] ?? A11Y_LABELS[normalized]?.[key] ?? ATTACHMENT_REJECTION_TEXTS[normalized]?.[key] ?? ONBOARDING_PLACEHOLDERS.en[key] ?? A11Y_LABELS.en[key] ?? ATTACHMENT_REJECTION_TEXTS.en[key] ?? EN[key] ?? key;
12088
12120
  }
12089
12121
  function createTranslator(locale) {
12090
12122
  const normalized = normalizeLocale(locale);
@@ -18840,6 +18872,54 @@
18840
18872
  });
18841
18873
  };
18842
18874
 
18875
+ // A file the chat will not take (over the conversation's upload limit, over
18876
+ // the per-file cap, or a type it does not accept) is answered with a message
18877
+ // from the agent, as the other channels do, instead of a warning chip under
18878
+ // the composer.
18879
+
18880
+ /** The rejections answered with a message; anything else (a network failure) keeps its chip so it can be retried. */
18881
+ const MESSAGE_REJECTION_CODES = new Set(["size_exceeded", "file_too_large", "mime_not_allowed"]);
18882
+
18883
+ // The short names a customer knows, for "You can send PDF, JPG, PNG".
18884
+ const TYPE_LABELS = [["application/pdf", "PDF"], ["image/jpeg", "JPG"], ["image/png", "PNG"], ["image/gif", "GIF"], ["image/webp", "WEBP"], ["application/msword", "DOC"], ["application/vnd.openxmlformats-officedocument.wordprocessingml.document", "DOCX"], ["application/vnd.ms-excel", "XLS"], ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "XLSX"], ["text/csv", "CSV"], ["text/plain", "TXT"]];
18885
+ function allowedTypesLabel(allowedMimes) {
18886
+ const allowed = new Set((allowedMimes || []).map(m => String(m).toLowerCase()));
18887
+ const labels = TYPE_LABELS.filter(([mime]) => allowed.has(mime)).map(([, label]) => label);
18888
+ return labels.join(", ");
18889
+ }
18890
+ const fill = (template, vars) => template.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
18891
+ const mb = bytes => {
18892
+ const v = Math.max(0, Number(bytes) || 0) / (1024 * 1024);
18893
+ return v >= 10 ? String(Math.round(v)) : String(Math.round(v * 10) / 10);
18894
+ };
18895
+
18896
+ /**
18897
+ * The agent's reply to a rejected file, in the widget's language.
18898
+ * `rejection`: { code, usedBytes } from useFileUpload; `config`: the upload
18899
+ * policy (fileUploadMaxSizeMB, fileUploadMaxFileMB, fileUploadAllowedMimes).
18900
+ */
18901
+ function attachmentRejectionText(t, rejection, config = {}) {
18902
+ const maxMb = String(config.fileUploadMaxSizeMB ?? "");
18903
+ if (rejection.code === "mime_not_allowed") {
18904
+ const types = allowedTypesLabel(config.fileUploadAllowedMimes);
18905
+ return fill(t("attachmentTypeNotAllowed"), {
18906
+ types: types || "PDF"
18907
+ });
18908
+ }
18909
+ if (rejection.code === "file_too_large") {
18910
+ return fill(t("attachmentFileTooLarge"), {
18911
+ max_mb: String(config.fileUploadMaxFileMB ?? maxMb)
18912
+ });
18913
+ }
18914
+ const used = Number(rejection.usedBytes) || 0;
18915
+ return used > 0 ? fill(t("attachmentLimitReachedUsed"), {
18916
+ max_mb: maxMb,
18917
+ used_mb: mb(used)
18918
+ }) : fill(t("attachmentLimitReached"), {
18919
+ max_mb: maxMb
18920
+ });
18921
+ }
18922
+
18843
18923
  /**
18844
18924
  * useFileUpload - attachment upload flow for the production widget.
18845
18925
  *
@@ -18875,7 +18955,11 @@
18875
18955
  case "size_exceeded":
18876
18956
  case "mime_not_allowed":
18877
18957
  case "storage_unavailable":
18958
+ case "file_too_large":
18878
18959
  return code;
18960
+ // The server's name for "over this conversation's upload limit".
18961
+ case "session_exceeded":
18962
+ return "size_exceeded";
18879
18963
  default:
18880
18964
  return "unknown";
18881
18965
  }
@@ -19053,7 +19137,11 @@
19053
19137
  config,
19054
19138
  apiEndpoint,
19055
19139
  authHeader,
19056
- onError
19140
+ onError,
19141
+ // Called instead of showing an error chip when the chat will not take the
19142
+ // file (MESSAGE_REJECTION_CODES): { code, filename, size, usedBytes }. The
19143
+ // widget answers with a message from the agent, as the other channels do.
19144
+ onRejected
19057
19145
  }) {
19058
19146
  const [attachments, setAttachments] = reactExports.useState([]);
19059
19147
  const inflightRef = reactExports.useRef(new Map());
@@ -19087,6 +19175,19 @@
19087
19175
  } : a));
19088
19176
  }, []);
19089
19177
  const errorPatch = reactExports.useCallback((id, code, message) => {
19178
+ // The server refused the file for the conversation's policy: no chip,
19179
+ // the agent says so instead.
19180
+ if (onRejected && MESSAGE_REJECTION_CODES.has(code)) {
19181
+ const target = attachmentsRef.current.find(a => a.id === id);
19182
+ setAttachments(prev => prev.filter(a => a.id !== id));
19183
+ onRejected({
19184
+ code,
19185
+ filename: target?.filename,
19186
+ size: target?.size,
19187
+ usedBytes: Math.max(0, sessionBytesRef.current.bytes)
19188
+ });
19189
+ return;
19190
+ }
19090
19191
  const patch = {
19091
19192
  status: "error",
19092
19193
  error: {
@@ -19103,7 +19204,7 @@
19103
19204
  if (target && onError) onError(target);
19104
19205
  return patched;
19105
19206
  });
19106
- }, [onError]);
19207
+ }, [onError, onRejected]);
19107
19208
  const validateFile = reactExports.useCallback((file, currentCount) => {
19108
19209
  if (!config?.fileUploadEnabled) {
19109
19210
  return {
@@ -19343,6 +19444,15 @@
19343
19444
  const currentCount = attachmentsRef.current.filter(a => a.status !== "error").length;
19344
19445
  const check = validateFile(file, currentCount);
19345
19446
  const id = nextId();
19447
+ if (!check.ok && onRejected && MESSAGE_REJECTION_CODES.has(check.code)) {
19448
+ onRejected({
19449
+ code: check.code,
19450
+ filename: file.name,
19451
+ size: file.size,
19452
+ usedBytes: Math.max(0, sessionBytesRef.current.bytes)
19453
+ });
19454
+ continue;
19455
+ }
19346
19456
  if (!check.ok) {
19347
19457
  const rejected = {
19348
19458
  id,
@@ -19381,7 +19491,7 @@
19381
19491
  sessionBytesRef.current.bytes += file.size;
19382
19492
  void doUpload(id, file);
19383
19493
  }
19384
- }, [validateFile, doUpload, nextId, onError]);
19494
+ }, [validateFile, doUpload, nextId, onError, onRejected]);
19385
19495
  const removeAttachment = reactExports.useCallback(id => {
19386
19496
  const xhr = inflightRef.current.get(id);
19387
19497
  if (xhr) {
@@ -19702,7 +19812,10 @@
19702
19812
  chatFeatures,
19703
19813
  apiEndpoint,
19704
19814
  organizationId,
19705
- conversationId
19815
+ conversationId,
19816
+ // Adds an agent message (useChatState.addAssistantMessage): a file the
19817
+ // chat will not take is answered this way, as on the other channels.
19818
+ onSynthesizeAssistantMessage
19706
19819
  }) => {
19707
19820
  const t = reactExports.useMemo(() => createTranslator(defaultLanguage), [defaultLanguage]);
19708
19821
  const poweredByParts = reactExports.useMemo(() => getPoweredByParts(defaultLanguage), [defaultLanguage]);
@@ -19736,7 +19849,14 @@
19736
19849
  conversationId,
19737
19850
  config: uploadConfig,
19738
19851
  apiEndpoint,
19739
- authHeader: undefined
19852
+ authHeader: undefined,
19853
+ // Over the upload limit, over the per-file cap or a type the chat does
19854
+ // not take: a message from the agent, not a warning chip.
19855
+ onRejected: onSynthesizeAssistantMessage ? rejection => onSynthesizeAssistantMessage(attachmentRejectionText(t, rejection, {
19856
+ ...uploadConfig,
19857
+ // The platform's per-file cap (backend CHAT_ATTACHMENT_PLATFORM_MAX_MB).
19858
+ fileUploadMaxFileMB: chatFeatures?.fileUploadMaxFileMB ?? 25
19859
+ })) : undefined
19740
19860
  });
19741
19861
  const fileInputRef = reactExports.useRef(null);
19742
19862
 
@@ -22500,6 +22620,7 @@
22500
22620
  })]
22501
22621
  })
22502
22622
  }), !(onboardingActive && !onboardingCompleted) && /*#__PURE__*/jsxRuntimeExports.jsx(ChatInput, {
22623
+ onSynthesizeAssistantMessage: onSynthesizeAssistantMessage,
22503
22624
  onStopGeneration: onStopGeneration,
22504
22625
  inputValue: inputValue,
22505
22626
  setInputValue: setInputValue,
@@ -22876,7 +22997,7 @@
22876
22997
  * value by its own test (that constant sat at 0.1.113 while this package was
22877
22998
  * at 0.2.2 precisely because nothing enforced it - EV-2431).
22878
22999
  */
22879
- const WIDGET_VERSION = '0.2.31';
23000
+ const WIDGET_VERSION = '0.2.32';
22880
23001
 
22881
23002
  /**
22882
23003
  * API utility functions