@getwidgets/live-chat-widget 1.1.4 → 1.1.6

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.
@@ -19710,6 +19710,12 @@
19710
19710
  function LuSend(props) {
19711
19711
  return GenIcon({ "attr": { "viewBox": "0 0 24 24", "fill": "none", "stroke": "currentColor", "strokeWidth": "2", "strokeLinecap": "round", "strokeLinejoin": "round" }, "child": [{ "tag": "path", "attr": { "d": "m22 2-7 20-4-9-9-4Z" } }, { "tag": "path", "attr": { "d": "M22 2 11 13" } }] })(props);
19712
19712
  }
19713
+ function ImAttachment(props) {
19714
+ return GenIcon({ "attr": { "version": "1.1", "viewBox": "0 0 16 16" }, "child": [{ "tag": "path", "attr": { "d": "M10.404 5.11l-1.015-1.014-5.075 5.074c-0.841 0.841-0.841 2.204 0 3.044s2.204 0.841 3.045 0l6.090-6.089c1.402-1.401 1.402-3.673 0-5.074s-3.674-1.402-5.075 0l-6.394 6.393c-0.005 0.005-0.010 0.009-0.014 0.013-1.955 1.955-1.955 5.123 0 7.077s5.123 1.954 7.078 0c0.004-0.004 0.008-0.009 0.013-0.014l0.001 0.001 4.365-4.364-1.015-1.014-4.365 4.363c-0.005 0.004-0.009 0.009-0.013 0.013-1.392 1.392-3.656 1.392-5.048 0s-1.392-3.655 0-5.047c0.005-0.005 0.009-0.009 0.014-0.013l-0.001-0.001 6.395-6.393c0.839-0.84 2.205-0.84 3.045 0s0.839 2.205 0 3.044l-6.090 6.089c-0.28 0.28-0.735 0.28-1.015 0s-0.28-0.735 0-1.014l5.075-5.075z" } }] })(props);
19715
+ }
19716
+ function RiChatSmileLine(props) {
19717
+ return GenIcon({ "attr": { "viewBox": "0 0 24 24" }, "child": [{ "tag": "path", "attr": { "d": "M6.45455 19L2 22.5V4C2 3.44772 2.44772 3 3 3H21C21.5523 3 22 3.44772 22 4V18C22 18.5523 21.5523 19 21 19H6.45455ZM5.76282 17H20V5H4V18.3851L5.76282 17ZM7 10H9C9 11.6569 10.3431 13 12 13C13.6569 13 15 11.6569 15 10H17C17 12.7614 14.7614 15 12 15C9.23858 15 7 12.7614 7 10Z" } }] })(props);
19718
+ }
19713
19719
  const API_BASE_URL = "https://api.getwidgets.app";
19714
19720
  const WS_BASE_URL = "wss://api.getwidgets.app";
19715
19721
  const fetchWidgetConfig = async (widgetId) => {
@@ -19751,7 +19757,7 @@
19751
19757
  return sessionId;
19752
19758
  };
19753
19759
  const LiveChatWidget = ({ widgetId, name: name2 = null, email = null, unique_id = null }) => {
19754
- var _a, _b;
19760
+ var _a, _b, _c;
19755
19761
  const [showMenu, setShowMenu] = reactExports.useState(false);
19756
19762
  const endChatButtonRef = reactExports.useRef(null);
19757
19763
  const [config, setConfig] = reactExports.useState(null);
@@ -19783,6 +19789,43 @@
19783
19789
  const [contactEmail, setContactEmail] = reactExports.useState("");
19784
19790
  const [showContactFields, setShowContactFields] = reactExports.useState(false);
19785
19791
  const [showContactErrors, setShowContactErrors] = reactExports.useState(false);
19792
+ const [selectedFile, setSelectedFile] = reactExports.useState(null);
19793
+ const [uploadingFile, setUploadingFile] = reactExports.useState(false);
19794
+ const [previewImage, setPreviewImage] = reactExports.useState(null);
19795
+ const [selectedFilePreviewUrl, setSelectedFilePreviewUrl] = reactExports.useState("");
19796
+ const [fileSizeError, setFileSizeError] = reactExports.useState(null);
19797
+ const fileInputRef = reactExports.useRef(null);
19798
+ const MAX_FILE_SIZE = 5 * 1024 * 1024;
19799
+ const validateFileSize = (file) => {
19800
+ if (file.size > MAX_FILE_SIZE) {
19801
+ setFileSizeError(`File size exceeds 5MB limit (${(file.size / (1024 * 1024)).toFixed(1)}MB)`);
19802
+ return false;
19803
+ }
19804
+ setFileSizeError(null);
19805
+ return true;
19806
+ };
19807
+ reactExports.useEffect(() => {
19808
+ const handlePaste = (e) => {
19809
+ if (selectedFile) return;
19810
+ if (e.clipboardData && e.clipboardData.files && e.clipboardData.files.length > 0) {
19811
+ const file = e.clipboardData.files[0];
19812
+ if (file.type.startsWith("image/")) {
19813
+ if (validateFileSize(file)) {
19814
+ setSelectedFile(file);
19815
+ setInputMessage("");
19816
+ e.preventDefault();
19817
+ }
19818
+ }
19819
+ }
19820
+ };
19821
+ const input = inputRef.current;
19822
+ if (input) input.addEventListener("paste", handlePaste);
19823
+ document.addEventListener("paste", handlePaste);
19824
+ return () => {
19825
+ if (input) input.removeEventListener("paste", handlePaste);
19826
+ document.removeEventListener("paste", handlePaste);
19827
+ };
19828
+ }, [selectedFile]);
19786
19829
  const base64UrlEncode = (value) => {
19787
19830
  const json = typeof value === "string" ? value : JSON.stringify(value);
19788
19831
  const base64 = btoa(unescape(encodeURIComponent(json)));
@@ -19800,24 +19843,40 @@
19800
19843
  const role2 = m2.sent_by === "user" ? "user" : "agent";
19801
19844
  const content22 = m2.message || m2.text || m2.content || "";
19802
19845
  const ts2 = m2.timestamp || m2.created_at || Date.now();
19803
- return {
19846
+ const normalized2 = {
19804
19847
  id: m2.id || m2.uuid,
19805
19848
  role: role2,
19806
19849
  content: content22,
19807
19850
  timestamp: new Date(ts2).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
19808
19851
  created_at: ts2
19809
19852
  };
19853
+ if (m2.is_file) {
19854
+ normalized2.is_file = true;
19855
+ normalized2.file_url = m2.file_url;
19856
+ normalized2.file_name = m2.file_name;
19857
+ normalized2.file_size = m2.file_size;
19858
+ normalized2.content_type = m2.content_type;
19859
+ }
19860
+ return normalized2;
19810
19861
  }
19811
19862
  const role = m2.role === "user" || m2.role === "visitor" ? "user" : m2.role || "agent";
19812
19863
  const content2 = m2.content || m2.message || m2.text || "";
19813
19864
  const ts = m2.created_at || m2.timestamp || Date.now();
19814
- return {
19865
+ const normalized = {
19815
19866
  id: m2.id || m2.uuid,
19816
19867
  role,
19817
19868
  content: content2,
19818
19869
  timestamp: new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
19819
19870
  created_at: ts
19820
19871
  };
19872
+ if (m2.is_file) {
19873
+ normalized.is_file = true;
19874
+ normalized.file_url = m2.file_url;
19875
+ normalized.file_name = m2.file_name;
19876
+ normalized.file_size = m2.file_size;
19877
+ normalized.content_type = m2.content_type;
19878
+ }
19879
+ return normalized;
19821
19880
  };
19822
19881
  reactExports.useEffect(() => {
19823
19882
  const handleResize = () => {
@@ -19839,6 +19898,18 @@
19839
19898
  const id2 = getSessionId();
19840
19899
  setSessionId(id2);
19841
19900
  }, [widgetId]);
19901
+ reactExports.useEffect(() => {
19902
+ var _a2;
19903
+ if (!selectedFile || !((_a2 = selectedFile.type) == null ? void 0 : _a2.startsWith("image/"))) {
19904
+ setSelectedFilePreviewUrl("");
19905
+ return;
19906
+ }
19907
+ const localUrl = URL.createObjectURL(selectedFile);
19908
+ setSelectedFilePreviewUrl(localUrl);
19909
+ return () => {
19910
+ URL.revokeObjectURL(localUrl);
19911
+ };
19912
+ }, [selectedFile]);
19842
19913
  reactExports.useEffect(() => {
19843
19914
  if (typeof window === "undefined") return;
19844
19915
  try {
@@ -19948,6 +20019,7 @@
19948
20019
  }
19949
20020
  }, []);
19950
20021
  const startLiveSession = async (widgetUuid, currentSessionId) => {
20022
+ var _a2;
19951
20023
  try {
19952
20024
  const url = `${API_BASE_URL}/api/widgets/livechat/start/${widgetUuid}/`;
19953
20025
  const storedAuthToken = authToken || (typeof window !== "undefined" ? localStorage.getItem("widgetkraft_livechat_authtoken") : null);
@@ -19961,7 +20033,14 @@
19961
20033
  throw new Error(txt || "Failed to start session");
19962
20034
  }
19963
20035
  const data = await res.json();
19964
- return data.session_id || data.id || currentSessionId;
20036
+ if (!(data == null ? void 0 : data.success)) {
20037
+ throw new Error("Session start API returned unsuccessful response");
20038
+ }
20039
+ const returnedSessionId = ((_a2 = data == null ? void 0 : data.data) == null ? void 0 : _a2.session_uuid) || (data == null ? void 0 : data.session_id) || (data == null ? void 0 : data.id) || currentSessionId;
20040
+ if (!returnedSessionId) {
20041
+ throw new Error("Session start API did not return a session id");
20042
+ }
20043
+ return returnedSessionId;
19965
20044
  } catch (e) {
19966
20045
  console.warn("startLiveSession failed", e);
19967
20046
  throw e;
@@ -20116,7 +20195,7 @@
20116
20195
  return false;
20117
20196
  };
20118
20197
  const initializeSession = async (firstMessage) => {
20119
- if (isSessionInitialized) return true;
20198
+ if (isSessionInitialized && sessionId) return sessionId;
20120
20199
  setIsConnecting(true);
20121
20200
  setLocalLoading(true);
20122
20201
  try {
@@ -20133,7 +20212,7 @@
20133
20212
  const sent = sendMessageOverWebSocket(firstMessage);
20134
20213
  if (!sent) {
20135
20214
  setLocalMessages((prev) => [...prev, {
20136
- id: `error-${Date.now()}`,
20215
+ id: v4(),
20137
20216
  role: "system",
20138
20217
  content: "Failed to send message. Please try again.",
20139
20218
  isError: true
@@ -20141,24 +20220,68 @@
20141
20220
  }
20142
20221
  }
20143
20222
  });
20144
- return true;
20223
+ return sid;
20145
20224
  } catch (error) {
20146
20225
  console.error("Failed to initialize session:", error);
20147
20226
  setLocalLoading(false);
20148
20227
  setIsConnecting(false);
20149
20228
  setLocalMessages((prev) => [...prev, {
20150
- id: `error-${Date.now()}`,
20229
+ id: v4(),
20151
20230
  role: "system",
20152
20231
  content: "Failed to connect to support. Please try again.",
20153
20232
  isError: true
20154
20233
  }]);
20234
+ return null;
20235
+ }
20236
+ };
20237
+ const uploadFile = async (file, caption) => {
20238
+ if (!file) return false;
20239
+ if (!validateFileSize(file)) {
20240
+ setUploadingFile(false);
20241
+ return false;
20242
+ }
20243
+ setUploadingFile(true);
20244
+ let activeSessionId = sessionId;
20245
+ if (!isSessionInitialized) {
20246
+ const initializedSessionId = await initializeSession("");
20247
+ if (!initializedSessionId) {
20248
+ setUploadingFile(false);
20249
+ return false;
20250
+ }
20251
+ activeSessionId = initializedSessionId;
20252
+ }
20253
+ if (!activeSessionId) {
20254
+ setUploadingFile(false);
20255
+ return false;
20256
+ }
20257
+ try {
20258
+ const formData = new FormData();
20259
+ formData.append("file", file);
20260
+ formData.append("message", caption || "");
20261
+ const uploadUrl = `${API_BASE_URL}/api/widgets/livechat/${activeSessionId}/upload/`;
20262
+ const response = await fetch(uploadUrl, {
20263
+ method: "POST",
20264
+ body: formData
20265
+ });
20266
+ if (response.status === 201) {
20267
+ setSelectedFile(null);
20268
+ setUploadingFile(false);
20269
+ return true;
20270
+ } else {
20271
+ console.error("File upload failed:", response.statusText);
20272
+ setUploadingFile(false);
20273
+ return false;
20274
+ }
20275
+ } catch (error) {
20276
+ console.error("File upload error:", error);
20277
+ setUploadingFile(false);
20155
20278
  return false;
20156
20279
  }
20157
20280
  };
20158
20281
  const performSend = async () => {
20159
20282
  const msg = (inputMessage || "").trim();
20160
20283
  if (!msg) return;
20161
- const localId = `local-${Date.now()}`;
20284
+ const localId = v4();
20162
20285
  const userMsg = {
20163
20286
  id: localId,
20164
20287
  role: "user",
@@ -20170,7 +20293,7 @@
20170
20293
  setInputMessage("");
20171
20294
  if (!isSessionInitialized) {
20172
20295
  setLocalMessages((prev) => [...prev, {
20173
- id: `sys-${Date.now()}`,
20296
+ id: v4(),
20174
20297
  role: "system",
20175
20298
  content: "Connecting to support..."
20176
20299
  }]);
@@ -20183,7 +20306,7 @@
20183
20306
  if (!sent) {
20184
20307
  setTimeout(() => {
20185
20308
  setLocalMessages((prev) => [...prev, {
20186
- id: `sys-${Date.now()}`,
20309
+ id: v4(),
20187
20310
  role: "system",
20188
20311
  content: "Reconnecting..."
20189
20312
  }]);
@@ -20194,7 +20317,7 @@
20194
20317
  const handleKeyPress = (e) => {
20195
20318
  if (e.key === "Enter" && !e.shiftKey) {
20196
20319
  e.preventDefault();
20197
- performSend();
20320
+ performSendWithContact();
20198
20321
  }
20199
20322
  };
20200
20323
  const toggleWidget = () => {
@@ -20236,16 +20359,22 @@
20236
20359
  const isMobile2 = windowWidth < 768;
20237
20360
  if (isMobile2) {
20238
20361
  return {
20239
- width: "95vw",
20240
- height: "85dvh",
20241
- maxWidth: "95vw",
20242
- maxHeight: "calc(100dvh - 24px)",
20243
- borderRadius: appearanceConfig.border_radius || "12px"
20362
+ width: "100vw",
20363
+ height: "100dvh",
20364
+ // Use dynamic viewport height to account for mobile browser UI
20365
+ maxWidth: "100vw",
20366
+ maxHeight: "100dvh",
20367
+ bottom: "0",
20368
+ right: "0",
20369
+ left: "0",
20370
+ borderRadius: "0",
20371
+ margin: "0",
20372
+ padding: "0"
20244
20373
  };
20245
20374
  }
20246
20375
  return {
20247
- width: appearanceConfig.width || "400px",
20248
- height: appearanceConfig.height || "600px",
20376
+ width: "430px",
20377
+ height: "700px",
20249
20378
  maxWidth: "90vw",
20250
20379
  maxHeight: "90vh"
20251
20380
  };
@@ -20254,16 +20383,19 @@
20254
20383
  const position2 = appearanceConfig.position || "bottom-right";
20255
20384
  const isMobile2 = windowWidth < 768;
20256
20385
  const baseStyles = { position: "fixed", zIndex: 9999 };
20257
- const edgeSpacing = isMobile2 ? "12px" : "20px";
20386
+ if (isMobile2 && isOpen) {
20387
+ return { ...baseStyles, left: 0, right: 0, bottom: 0, top: 0, margin: 0, padding: 0 };
20388
+ }
20389
+ const spacing = isMobile2 ? "16px" : "20px";
20258
20390
  switch (position2) {
20259
20391
  case "bottom-left":
20260
- return { ...baseStyles, left: edgeSpacing, bottom: edgeSpacing };
20392
+ return { ...baseStyles, left: spacing, bottom: spacing };
20261
20393
  case "top-left":
20262
- return { ...baseStyles, left: edgeSpacing, top: edgeSpacing };
20394
+ return { ...baseStyles, left: spacing, top: spacing };
20263
20395
  case "top-right":
20264
- return { ...baseStyles, right: edgeSpacing, top: edgeSpacing };
20396
+ return { ...baseStyles, right: spacing, top: spacing };
20265
20397
  default:
20266
- return { ...baseStyles, right: edgeSpacing, bottom: edgeSpacing };
20398
+ return { ...baseStyles, right: spacing, bottom: spacing };
20267
20399
  }
20268
20400
  };
20269
20401
  const responsiveDimensions = getResponsiveDimensions();
@@ -20309,12 +20441,7 @@
20309
20441
  setShowContactFields(false);
20310
20442
  }
20311
20443
  }, [inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email, authToken, isOpen]);
20312
- const isValidEmail = (value) => {
20313
- if (!value) return false;
20314
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value).trim());
20315
- };
20316
- const emailIsInvalid = showContactFields && contactEmail.trim() && !isValidEmail(contactEmail);
20317
- const isContactValid = !(inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) || (!showContactFields ? true : contactName.trim() && contactEmail.trim() && isValidEmail(contactEmail));
20444
+ const isContactValid = !(inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) || (!showContactFields ? true : contactName.trim() && contactEmail.trim());
20318
20445
  const createAndStoreAuthToken = () => {
20319
20446
  const payload = { name: contactName.trim(), email: contactEmail.trim(), unique_id };
20320
20447
  const token = buildUnsignedJwt(payload);
@@ -20329,14 +20456,21 @@
20329
20456
  if ((inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) && !authToken) {
20330
20457
  const missingName = !contactName.trim();
20331
20458
  const missingEmail = !contactEmail.trim();
20332
- const invalidEmail = !missingEmail && !isValidEmail(contactEmail);
20333
- if (missingName || missingEmail || invalidEmail) {
20459
+ if (missingName || missingEmail) {
20334
20460
  setShowContactErrors(true);
20335
20461
  return;
20336
20462
  }
20337
20463
  createAndStoreAuthToken();
20338
20464
  setShowContactFields(false);
20339
20465
  }
20466
+ if (selectedFile) {
20467
+ const success = await uploadFile(selectedFile, inputMessage);
20468
+ if (success) {
20469
+ setInputMessage("");
20470
+ if (fileInputRef.current) fileInputRef.current.value = "";
20471
+ }
20472
+ return;
20473
+ }
20340
20474
  await performSend();
20341
20475
  };
20342
20476
  reactExports.useEffect(() => {
@@ -20374,38 +20508,28 @@
20374
20508
  return () => btn.removeEventListener("click", handleClick, true);
20375
20509
  }, []);
20376
20510
  if (!config) return null;
20377
- const handleStartNewChat = () => {
20378
- try {
20379
- const newSessionId = v4();
20380
- if (wsRef.current) {
20381
- try {
20382
- wsRef.current.close();
20383
- } catch (e) {
20384
- }
20385
- wsRef.current = null;
20511
+ const handleEndChat = () => {
20512
+ if (wsRef.current) {
20513
+ try {
20514
+ wsRef.current.close();
20515
+ } catch (e) {
20386
20516
  }
20517
+ wsRef.current = null;
20518
+ }
20519
+ const newSessionId = v4();
20520
+ setSessionId(newSessionId);
20521
+ setLocalMessages([]);
20522
+ setInputMessage("");
20523
+ setIsSessionInitialized(false);
20524
+ setHasCheckedSession(false);
20525
+ try {
20387
20526
  localStorage.setItem("live-chat-widget/session-id", newSessionId);
20388
- setSessionId(newSessionId);
20389
- setHasCheckedSession(false);
20390
- setIsConnecting(false);
20391
- setLocalLoading(false);
20392
- setAuthToken(null);
20393
- setMessages([]);
20394
- setLocalMessages([]);
20395
- setInputMessage("");
20396
- setIsSessionInitialized(false);
20397
- setShowMenu(false);
20398
- setContactName("");
20399
- setContactEmail("");
20400
- setShowContactFields(!!(inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email));
20401
20527
  } catch (e) {
20402
20528
  }
20403
- };
20404
- const handleEndChat = () => {
20405
- handleStartNewChat();
20529
+ setShowMenu(false);
20406
20530
  };
20407
20531
  const MenuDotsIcon = ({ color: color2 = "#fff", size = 22 }) => /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "5", r: "2", fill: color2 }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "2", fill: color2 }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "19", r: "2", fill: color2 }));
20408
- return /* @__PURE__ */ React.createElement("div", { style: getPositionStyles() }, !isOpen && showNotificationPreview && /* @__PURE__ */ React.createElement(
20532
+ return /* @__PURE__ */ React.createElement("div", { style: getPositionStyles() }, !isOpen && showNotificationPreview && !isMobile && /* @__PURE__ */ React.createElement(
20409
20533
  "div",
20410
20534
  {
20411
20535
  role: "button",
@@ -20446,10 +20570,10 @@
20446
20570
  },
20447
20571
  unreadCount > 99 ? "99+" : unreadCount
20448
20572
  ),
20449
- /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 flex-1" }, headerConfig.logo_url ? /* @__PURE__ */ React.createElement(
20573
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 flex-1" }, headerConfig.square_logo_url ? /* @__PURE__ */ React.createElement(
20450
20574
  "img",
20451
20575
  {
20452
- src: headerConfig.logo_url,
20576
+ src: headerConfig.square_logo_url,
20453
20577
  alt: "Chat",
20454
20578
  className: "responsive-notification-logo",
20455
20579
  style: {
@@ -20470,18 +20594,7 @@
20470
20594
  flexShrink: 0
20471
20595
  }
20472
20596
  },
20473
- /* @__PURE__ */ React.createElement(
20474
- "svg",
20475
- {
20476
- viewBox: "0 0 24 24",
20477
- fill: "currentColor",
20478
- style: {
20479
- width: "16px",
20480
- height: "16px"
20481
- }
20482
- },
20483
- /* @__PURE__ */ React.createElement("path", { d: "M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 9h12v2H6V9zm8 5H6v-2h8v2zm4-6H6V6h12v2z" })
20484
- )
20597
+ /* @__PURE__ */ React.createElement(RiChatSmileLine, { color: headerConfig.font_color || "#FFFFFF", size: 20 })
20485
20598
  ), /* @__PURE__ */ React.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React.createElement("h3", { className: "font-semibold text-sm", style: headerConfig.font_color ? { color: headerConfig.font_color } : { color: "black" } }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "text-xs opacity-70", style: headerConfig.font_color ? { color: headerConfig.font_color } : { color: "black" } }, "Just now"))), /* @__PURE__ */ React.createElement(
20486
20599
  "button",
20487
20600
  {
@@ -20492,7 +20605,7 @@
20492
20605
  "×"
20493
20606
  )),
20494
20607
  /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-relaxed text-gray-200", style: { fontSize: isMobile ? "13px" : "14px", color: headerConfig.font_color ? headerConfig.font_color : "#000000" } }, (latestMessage == null ? void 0 : latestMessage.content) || (inputAreaConfig == null ? void 0 : inputAreaConfig.welcome_message) || (inputAreaConfig == null ? void 0 : inputAreaConfig.first_ai_message) || headerConfig.subtitle || "Connect with visitors")
20495
- ), !isOpen && !showNotificationPreview && /* @__PURE__ */ React.createElement(
20608
+ ), !isOpen && (!showNotificationPreview || isMobile) && /* @__PURE__ */ React.createElement(
20496
20609
  "button",
20497
20610
  {
20498
20611
  onClick: toggleWidget,
@@ -20507,17 +20620,17 @@
20507
20620
  margin: isMobile ? "16px" : "0"
20508
20621
  }
20509
20622
  },
20510
- headerConfig.logo_url ? /* @__PURE__ */ React.createElement(
20623
+ headerConfig.square_logo_url ? /* @__PURE__ */ React.createElement(
20511
20624
  "img",
20512
20625
  {
20513
- src: headerConfig.logo_url,
20626
+ src: headerConfig.square_logo_url,
20514
20627
  alt: "logo",
20515
20628
  style: {
20516
20629
  width: "auto",
20517
20630
  height: `${headerConfig.logo_size || 32}px`
20518
20631
  }
20519
20632
  }
20520
- ) : /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", style: { width: isMobile ? 18 : 20, height: isMobile ? 18 : 20 } }, /* @__PURE__ */ React.createElement("path", { d: "M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 9h12v2H6V9zm8 5H6v-2h8v2zm4-6H6V6h12v2z" })),
20633
+ ) : /* @__PURE__ */ React.createElement(RiChatSmileLine, { color: headerConfig.font_color || "#FFFFFF", size: isMobile ? 24 : 26 }),
20521
20634
  unreadCount > 0 && /* @__PURE__ */ React.createElement(
20522
20635
  "div",
20523
20636
  {
@@ -20536,16 +20649,18 @@
20536
20649
  ), isOpen && /* @__PURE__ */ React.createElement(
20537
20650
  "div",
20538
20651
  {
20539
- className: "rounded-xl shadow-lg overflow-hidden transition-all duration-300 flex flex-col",
20652
+ className: "overflow-hidden transition-all duration-300 flex flex-col",
20540
20653
  style: {
20541
20654
  width: responsiveDimensions.width,
20542
20655
  height: responsiveDimensions.height,
20543
20656
  maxWidth: responsiveDimensions.maxWidth,
20544
20657
  maxHeight: responsiveDimensions.maxHeight,
20545
20658
  backgroundColor: appearanceConfig.background_color || "#F9FAFB",
20546
- borderRadius: isMobile ? responsiveDimensions.borderRadius : appearanceConfig.border_radius || "16px",
20659
+ borderRadius: isMobile ? "0" : appearanceConfig.border_radius || "16px",
20547
20660
  fontFamily: headerConfig.font_family || "'Inter', sans-serif",
20548
- ...isMobile ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 } : {}
20661
+ display: "flex",
20662
+ flexDirection: "column",
20663
+ boxShadow: isMobile ? "none" : "0 10px 25px rgba(0, 0, 0, 0.1)"
20549
20664
  }
20550
20665
  },
20551
20666
  /* @__PURE__ */ React.createElement(
@@ -20555,14 +20670,18 @@
20555
20670
  style: {
20556
20671
  backgroundColor: headerConfig.background_color || "#111827",
20557
20672
  color: headerConfig.font_color || "#FFFFFF",
20558
- padding: isMobile ? "12px 16px" : "16px 20px",
20559
- minHeight: "auto"
20673
+ padding: isMobile ? "10px 12px" : "16px 20px",
20674
+ minHeight: "auto",
20675
+ display: "flex",
20676
+ alignItems: "center",
20677
+ justifyContent: "space-between"
20560
20678
  }
20561
20679
  },
20562
- /* @__PURE__ */ React.createElement(
20680
+ /* @__PURE__ */ React.createElement("div", { className: "relative", style: { width: isMobile ? "40px" : "36px" } }, /* @__PURE__ */ React.createElement(
20563
20681
  "button",
20564
20682
  {
20565
- className: "livechat-menu-trigger absolute left-3 top-1/2 transform -translate-y-1/2 hover:opacity-80 transition-opacity",
20683
+ onClick: () => setShowMenu(!showMenu),
20684
+ className: "hover:opacity-80 transition-opacity livechat-menu-trigger",
20566
20685
  style: {
20567
20686
  background: "none",
20568
20687
  border: "none",
@@ -20573,74 +20692,66 @@
20573
20692
  alignItems: "center",
20574
20693
  justifyContent: "center",
20575
20694
  padding: 0,
20576
- zIndex: 20
20577
- },
20578
- onClick: (e) => {
20579
- e.stopPropagation();
20580
- setShowMenu((v2) => !v2);
20695
+ color: getContrastColor(headerConfig.background_color || "#111827")
20581
20696
  },
20582
- "aria-label": "Open menu"
20697
+ "aria-label": "Menu"
20583
20698
  },
20584
- /* @__PURE__ */ React.createElement(MenuDotsIcon, { color: getContrastColor(headerConfig.background_color || "#111827"), size: isMobile ? 22 : 22 })
20585
- ),
20586
- showMenu && /* @__PURE__ */ React.createElement(
20699
+ /* @__PURE__ */ React.createElement(MenuDotsIcon, { color: getContrastColor(headerConfig.background_color || "#111827"), size: isMobile ? 22 : 20 })
20700
+ ), showMenu && /* @__PURE__ */ React.createElement(
20587
20701
  "div",
20588
20702
  {
20589
- className: "livechat-menu-popup absolute left-3 top-12 bg-white rounded shadow-lg border",
20703
+ className: "livechat-menu-popup",
20590
20704
  style: {
20591
- minWidth: isMobile ? "132px" : "120px",
20592
- top: isMobile ? "calc(50% + 18px)" : "calc(50% + 16px)",
20593
- left: isMobile ? "8px" : "12px",
20594
- zIndex: 30,
20595
- boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
20596
- border: "1px solid #e5e7eb",
20597
- padding: "6px 0",
20598
- color: "#111827",
20599
- fontSize: isMobile ? "16px" : "15px",
20600
- fontWeight: 500
20705
+ position: "absolute",
20706
+ left: "8px",
20707
+ top: "calc(100% + 4px)",
20708
+ backgroundColor: "#FFFFFF",
20709
+ border: "1px solid #E5E7EB",
20710
+ borderRadius: "8px",
20711
+ boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
20712
+ zIndex: 1e3,
20713
+ minWidth: "140px"
20601
20714
  }
20602
20715
  },
20603
20716
  /* @__PURE__ */ React.createElement(
20604
20717
  "button",
20605
20718
  {
20606
20719
  ref: endChatButtonRef,
20607
- className: "transition-all duration-200 hover:bg-red-50",
20608
- type: "button",
20609
- tabIndex: "0",
20720
+ onClick: () => {
20721
+ handleEndChat();
20722
+ },
20610
20723
  style: {
20611
- background: "none",
20612
- border: "none",
20613
20724
  width: "100%",
20614
- cursor: "pointer",
20615
- padding: isMobile ? "8px 12px" : "6px 12px",
20616
- minHeight: "32px",
20617
- fontSize: isMobile ? "14px" : "13px",
20618
- color: "#DC2626",
20725
+ padding: "10px 14px",
20726
+ border: "none",
20727
+ backgroundColor: "transparent",
20728
+ color: "#EF4444",
20729
+ fontSize: "14px",
20619
20730
  fontWeight: "500",
20620
- borderRadius: "6px",
20621
- margin: "0",
20622
- display: "flex",
20623
- alignItems: "center",
20624
- justifyContent: "center",
20625
- userSelect: "none"
20731
+ cursor: "pointer",
20732
+ textAlign: "left",
20733
+ borderRadius: "8px"
20626
20734
  },
20627
- onClick: (e) => {
20628
- var _a2, _b2;
20629
- e.preventDefault();
20630
- e.stopPropagation();
20631
- (_b2 = (_a2 = e.nativeEvent) == null ? void 0 : _a2.stopImmediatePropagation) == null ? void 0 : _b2.call(_a2);
20632
- handleEndChat();
20633
- setTimeout(() => setShowMenu(false), 0);
20634
- }
20735
+ onMouseOver: (e) => e.currentTarget.style.backgroundColor = "rgba(239, 68, 68, 0.1)",
20736
+ onMouseOut: (e) => e.currentTarget.style.backgroundColor = "transparent"
20635
20737
  },
20636
20738
  "End Chat"
20637
20739
  )
20638
- ),
20740
+ )),
20741
+ /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center justify-center flex-1" }, headerConfig.logo_url && /* @__PURE__ */ React.createElement(
20742
+ "img",
20743
+ {
20744
+ src: headerConfig.logo_url,
20745
+ alt: "logo",
20746
+ className: "w-auto mx-auto mb-2",
20747
+ style: { height: `${headerConfig.logo_size || 32}px` }
20748
+ }
20749
+ ), !headerConfig.hide_title && /* @__PURE__ */ React.createElement("h2", { className: "font-semibold leading-tight", style: { fontSize: isMobile ? "14px" : "16px", margin: "0" } }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "opacity-80 leading-tight", style: { fontSize: isMobile ? "11px" : "13px", margin: "2px 0 0 0" } }, headerConfig.subtitle || "Connect with visitors")),
20639
20750
  /* @__PURE__ */ React.createElement(
20640
20751
  "button",
20641
20752
  {
20642
20753
  onClick: toggleWidget,
20643
- className: "absolute right-3 top-1/2 transform -translate-y-1/2 hover:opacity-80 transition-opacity",
20754
+ className: "hover:opacity-80 transition-opacity",
20644
20755
  style: {
20645
20756
  background: "none",
20646
20757
  border: "none",
@@ -20656,16 +20767,7 @@
20656
20767
  }
20657
20768
  },
20658
20769
  "×"
20659
- ),
20660
- /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center justify-center" }, headerConfig.logo_url && /* @__PURE__ */ React.createElement(
20661
- "img",
20662
- {
20663
- src: headerConfig.logo_url,
20664
- alt: "logo",
20665
- className: "w-auto mx-auto mb-2",
20666
- style: { height: `${headerConfig.logo_size || 32}px` }
20667
- }
20668
- ), !headerConfig.hide_title && /* @__PURE__ */ React.createElement("h2", { className: "font-semibold leading-tight", style: { fontSize: isMobile ? "14px" : "16px", margin: "0" } }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "opacity-80 leading-tight", style: { fontSize: isMobile ? "11px" : "13px", margin: "2px 0 0 0" } }, headerConfig.subtitle || "Connect with visitors"))
20770
+ )
20669
20771
  ),
20670
20772
  /* @__PURE__ */ React.createElement(
20671
20773
  "div",
@@ -20685,9 +20787,10 @@
20685
20787
  opacity: appearanceConfig.background_opacity ?? 1,
20686
20788
  zIndex: 0
20687
20789
  } }),
20688
- /* @__PURE__ */ React.createElement("div", { className: "relative z-10 p-3 sm:p-4 space-y-2 sm:space-y-3", style: {
20790
+ /* @__PURE__ */ React.createElement("div", { className: "relative z-10 space-y-2 sm:space-y-3", style: {
20689
20791
  backgroundColor: appearanceConfig.inner_background_color !== void 0 ? appearanceConfig.inner_background_color : "transparent",
20690
- paddingBottom: isMobile ? "20px" : "16px"
20792
+ padding: isMobile ? "8px 10px" : "12px 16px",
20793
+ paddingBottom: isMobile ? "12px" : "16px"
20691
20794
  } }, combinedMessages.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "flex justify-start" }, /* @__PURE__ */ React.createElement(
20692
20795
  "div",
20693
20796
  {
@@ -20704,7 +20807,84 @@
20704
20807
  inputAreaConfig.welcome_message || inputAreaConfig.first_ai_message || "Hello! How can I assist you today?"
20705
20808
  )) : combinedMessages.map((message) => {
20706
20809
  const timeText = getMessageTime(message);
20707
- return /* @__PURE__ */ React.createElement("div", { key: message.id, className: `flex flex-col ${message.role === "user" ? "items-end" : "items-start"}` }, /* @__PURE__ */ React.createElement(
20810
+ return /* @__PURE__ */ React.createElement("div", { key: message.id, className: `flex flex-col ${message.role === "user" ? "items-end" : "items-start"}` }, message.is_file && message.file_url ? /* @__PURE__ */ React.createElement(
20811
+ "div",
20812
+ {
20813
+ className: `flex flex-col gap-2 ${message.role === "user" ? "items-end" : "items-start"}`,
20814
+ style: { marginBottom: 4 }
20815
+ },
20816
+ /* @__PURE__ */ React.createElement(
20817
+ "div",
20818
+ {
20819
+ className: "rounded-xl overflow-hidden cursor-pointer hover:opacity-95 transition-opacity border shadow-sm",
20820
+ onClick: () => setPreviewImage(message.file_url),
20821
+ style: {
20822
+ background: "#fff",
20823
+ display: "block",
20824
+ maxWidth: isMobile ? 180 : 220,
20825
+ maxHeight: 160,
20826
+ minWidth: 64,
20827
+ minHeight: 48,
20828
+ borderRadius: 18,
20829
+ border: "1.5px solid #e5e7eb",
20830
+ boxShadow: "0 2px 8px 0 rgba(0,0,0,0.06)",
20831
+ padding: 0,
20832
+ marginLeft: message.role === "user" ? "auto" : void 0,
20833
+ marginRight: message.role === "user" ? 0 : void 0
20834
+ }
20835
+ },
20836
+ message.content_type && message.content_type.startsWith("image/") ? /* @__PURE__ */ React.createElement(
20837
+ "img",
20838
+ {
20839
+ src: message.file_url,
20840
+ alt: message.file_name,
20841
+ style: {
20842
+ width: "100%",
20843
+ height: "auto",
20844
+ maxHeight: 160,
20845
+ objectFit: "cover",
20846
+ borderRadius: 18,
20847
+ aspectRatio: "4/3",
20848
+ background: "#f3f4f6",
20849
+ display: "block",
20850
+ transition: "box-shadow 0.2s"
20851
+ }
20852
+ }
20853
+ ) : /* @__PURE__ */ React.createElement(
20854
+ "div",
20855
+ {
20856
+ className: "flex flex-col items-center justify-center gap-1",
20857
+ style: {
20858
+ width: 120,
20859
+ height: 64,
20860
+ background: "#f3f4f6",
20861
+ borderRadius: 16,
20862
+ padding: 8
20863
+ }
20864
+ },
20865
+ /* @__PURE__ */ React.createElement(ImAttachment, { style: { fontSize: 25, color: getContrastColor(inputAreaConfig.background_color) } }),
20866
+ /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12, color: "#374151", wordBreak: "break-all", textAlign: "center" } }, message.file_name)
20867
+ )
20868
+ ),
20869
+ message.content && /* @__PURE__ */ React.createElement(
20870
+ "div",
20871
+ {
20872
+ className: "px-3 py-1 rounded-lg mt-1",
20873
+ style: {
20874
+ backgroundColor: message.role === "user" ? chatAreaConfig.user_response_color || "#4ADE80" : chatAreaConfig.ai_response_color || "#E2E8F0",
20875
+ borderRadius: chatAreaConfig.chat_bubble_radius || "16px",
20876
+ fontSize: isMobile ? "13px" : "14px",
20877
+ color: message.role === "user" ? chatAreaConfig.user_font_color || "#111827" : chatAreaConfig.ai_font_color || "#111827",
20878
+ maxWidth: isMobile ? 180 : 220,
20879
+ marginLeft: message.role === "user" ? "auto" : void 0,
20880
+ marginRight: message.role === "user" ? 0 : void 0,
20881
+ wordBreak: "break-word",
20882
+ textAlign: "center"
20883
+ }
20884
+ },
20885
+ /* @__PURE__ */ React.createElement(Markdown, { remarkPlugins: [remarkGfm] }, message.content)
20886
+ )
20887
+ ) : /* @__PURE__ */ React.createElement(
20708
20888
  "div",
20709
20889
  {
20710
20890
  className: `p-3 inline-block rounded-lg`,
@@ -20741,51 +20921,112 @@
20741
20921
  style: {
20742
20922
  backgroundColor: inputAreaConfig.background_color || "#FFF",
20743
20923
  borderColor: inputAreaConfig.border_color || "#D1D5DB",
20744
- minHeight: isMobile ? 64 : 70,
20924
+ minHeight: "auto",
20745
20925
  position: "relative",
20746
- padding: isMobile ? "10px 12px" : "12px 16px",
20747
- gap: isMobile ? "8px" : "12px"
20926
+ padding: isMobile ? "8px 10px" : "12px 16px",
20927
+ gap: isMobile ? "6px" : "12px"
20748
20928
  }
20749
20929
  },
20750
20930
  /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 w-full" }, /* @__PURE__ */ React.createElement(
20751
20931
  "input",
20932
+ {
20933
+ ref: fileInputRef,
20934
+ type: "file",
20935
+ onChange: (e) => {
20936
+ if (e.target.files && e.target.files[0]) {
20937
+ const file = e.target.files[0];
20938
+ if (validateFileSize(file)) {
20939
+ setSelectedFile(file);
20940
+ setInputMessage("");
20941
+ }
20942
+ }
20943
+ },
20944
+ style: { display: "none" },
20945
+ accept: "*/*"
20946
+ }
20947
+ ), /* @__PURE__ */ React.createElement(
20948
+ "button",
20949
+ {
20950
+ onClick: () => {
20951
+ var _a2;
20952
+ return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
20953
+ },
20954
+ disabled: uploadingFile || localLoading || selectedFile !== null,
20955
+ className: "rounded-full flex items-center justify-center flex-shrink-0 hover:opacity-90 transition-opacity",
20956
+ title: "Upload file",
20957
+ style: {
20958
+ backgroundColor: selectedFile ? ((_b = inputAreaConfig.send_button) == null ? void 0 : _b.color) || "#2563EB" : inputAreaConfig.border_color || "#D1D5DB",
20959
+ width: "40px",
20960
+ height: "40px",
20961
+ minWidth: "40px",
20962
+ minHeight: "40px",
20963
+ opacity: uploadingFile || selectedFile ? 0.5 : 1,
20964
+ transition: "opacity 0.2s",
20965
+ border: "none",
20966
+ cursor: selectedFile ? "not-allowed" : "pointer",
20967
+ display: "flex",
20968
+ alignItems: "center",
20969
+ justifyContent: "center"
20970
+ }
20971
+ },
20972
+ uploadingFile ? /* @__PURE__ */ React.createElement(
20973
+ "div",
20974
+ {
20975
+ className: "rounded-full",
20976
+ style: {
20977
+ width: 16,
20978
+ height: 16,
20979
+ border: "2px solid #FFFFFF",
20980
+ borderTop: "2px solid transparent",
20981
+ animation: "spin 0.8s linear infinite"
20982
+ }
20983
+ }
20984
+ ) : /* @__PURE__ */ React.createElement("span", { style: { fontSize: "16px" } }, /* @__PURE__ */ React.createElement(ImAttachment, { style: { color: getContrastColor(inputAreaConfig.background_color) } }))
20985
+ ), /* @__PURE__ */ React.createElement(
20986
+ "textarea",
20752
20987
  {
20753
20988
  ref: inputRef,
20754
20989
  value: inputMessage,
20755
20990
  onChange: (e) => setInputMessage(e.target.value),
20756
- onKeyPress: handleKeyPress,
20757
- disabled: localLoading,
20758
- className: "flex-1 px-3 py-2 border rounded-lg",
20991
+ onKeyDown: handleKeyPress,
20992
+ disabled: localLoading || uploadingFile,
20993
+ className: "flex-1 px-3 py-2 border rounded-lg resize-none custom-chat-textarea",
20759
20994
  style: {
20760
20995
  borderColor: inputAreaConfig.border_color || "#D1D5DB",
20761
20996
  color: inputAreaConfig.text_color || "#111827",
20762
20997
  fontSize: isMobile ? "14px" : "14px",
20763
20998
  backgroundColor: inputAreaConfig.background_color || "#FFF",
20764
20999
  minHeight: "40px",
21000
+ maxHeight: "120px",
21001
+ // 4 rows approx
20765
21002
  padding: isMobile ? "8px 12px" : "10px 12px",
20766
- lineHeight: "1.5"
21003
+ lineHeight: "1.5",
21004
+ overflowY: "auto",
21005
+ boxSizing: "border-box"
20767
21006
  },
20768
- placeholder: inputAreaConfig.placeholder_text || "Type your message..."
21007
+ placeholder: selectedFile ? "Add a caption (optional)" : inputAreaConfig.placeholder_text || "Type your message...",
21008
+ rows: 1,
21009
+ maxLength: 2e3
20769
21010
  }
20770
21011
  ), /* @__PURE__ */ React.createElement(
20771
21012
  "button",
20772
21013
  {
20773
21014
  onClick: performSendWithContact,
20774
- disabled: localLoading || !inputMessage.trim(),
21015
+ disabled: localLoading || uploadingFile || !selectedFile && !inputMessage.trim() || !isContactValid,
20775
21016
  className: "rounded-full flex items-center justify-center flex-shrink-0 hover:opacity-90 transition-opacity",
20776
21017
  style: {
20777
- backgroundColor: ((_b = inputAreaConfig.send_button) == null ? void 0 : _b.color) || "#2563EB",
21018
+ backgroundColor: ((_c = inputAreaConfig.send_button) == null ? void 0 : _c.color) || "#2563EB",
20778
21019
  width: `${sendButtonSize}px`,
20779
21020
  height: `${sendButtonSize}px`,
20780
21021
  minWidth: `${sendButtonSize}px`,
20781
21022
  minHeight: `${sendButtonSize}px`,
20782
- opacity: localLoading || !inputMessage.trim() || !isContactValid ? 0.5 : 1,
21023
+ opacity: localLoading || uploadingFile || !selectedFile && !inputMessage.trim() || !isContactValid ? 0.5 : 1,
20783
21024
  transition: "opacity 0.2s",
20784
21025
  border: "none",
20785
21026
  cursor: "pointer"
20786
21027
  }
20787
21028
  },
20788
- localLoading ? /* @__PURE__ */ React.createElement(
21029
+ localLoading || uploadingFile ? /* @__PURE__ */ React.createElement(
20789
21030
  "div",
20790
21031
  {
20791
21032
  className: "rounded-full animate-spin",
@@ -20797,10 +21038,10 @@
20797
21038
  }
20798
21039
  }
20799
21040
  ) : (() => {
20800
- var _a2, _b2, _c;
21041
+ var _a2, _b2, _c2;
20801
21042
  const iconComponentKey = (_a2 = inputAreaConfig == null ? void 0 : inputAreaConfig.send_button) == null ? void 0 : _a2.icon_component;
20802
21043
  const iconUrl = (_b2 = inputAreaConfig == null ? void 0 : inputAreaConfig.send_button) == null ? void 0 : _b2.icon_url;
20803
- const iconColor = ((_c = inputAreaConfig == null ? void 0 : inputAreaConfig.send_button) == null ? void 0 : _c.icon_color) || "#FFFFFF";
21044
+ const iconColor = ((_c2 = inputAreaConfig == null ? void 0 : inputAreaConfig.send_button) == null ? void 0 : _c2.icon_color) || "#FFFFFF";
20804
21045
  if (iconComponentKey && ICON_MAP[iconComponentKey]) {
20805
21046
  const Icon2 = ICON_MAP[iconComponentKey];
20806
21047
  return /* @__PURE__ */ React.createElement(Icon2, { className: "w-4 h-4", style: { color: iconColor } });
@@ -20809,8 +21050,110 @@
20809
21050
  return /* @__PURE__ */ React.createElement(Send, { className: "w-4 h-4", style: { color: "#FFFFFF" } });
20810
21051
  })()
20811
21052
  )),
21053
+ fileSizeError && /* @__PURE__ */ React.createElement("div", { style: {
21054
+ padding: isMobile ? "8px 10px" : "10px 12px",
21055
+ marginBottom: "6px",
21056
+ borderRadius: "6px",
21057
+ border: "1px solid #EF4444",
21058
+ backgroundColor: "#FEE2E2",
21059
+ fontSize: isMobile ? "12px" : "13px",
21060
+ color: "#991B1B",
21061
+ display: "flex",
21062
+ alignItems: "center",
21063
+ justifyContent: "space-between",
21064
+ gap: "8px"
21065
+ } }, /* @__PURE__ */ React.createElement("span", null, fileSizeError), /* @__PURE__ */ React.createElement(
21066
+ "button",
21067
+ {
21068
+ onClick: () => setFileSizeError(null),
21069
+ style: {
21070
+ background: "none",
21071
+ border: "none",
21072
+ cursor: "pointer",
21073
+ color: "#991B1B",
21074
+ fontSize: isMobile ? "14px" : "16px",
21075
+ padding: "0",
21076
+ minWidth: "auto"
21077
+ },
21078
+ "aria-label": "Close error"
21079
+ },
21080
+ "×"
21081
+ )),
21082
+ selectedFile && /* @__PURE__ */ React.createElement("div", { className: "border rounded-lg", style: {
21083
+ borderColor: inputAreaConfig.border_color || "#D1D5DB",
21084
+ backgroundColor: inputAreaConfig.background_color || "#FFF",
21085
+ padding: isMobile ? "8px" : "12px",
21086
+ maxWidth: isMobile ? "100%" : 220,
21087
+ marginBottom: "4px"
21088
+ } }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between mb-2" }, /* @__PURE__ */ React.createElement("div", { style: { color: inputAreaConfig.text_color || "#111827", fontSize: isMobile ? "12px" : "13px", fontWeight: "600" } }, "Selected file"), /* @__PURE__ */ React.createElement(
21089
+ "button",
21090
+ {
21091
+ onClick: () => {
21092
+ setSelectedFile(null);
21093
+ setInputMessage("");
21094
+ if (fileInputRef.current) fileInputRef.current.value = "";
21095
+ },
21096
+ style: {
21097
+ background: "none",
21098
+ border: "none",
21099
+ cursor: "pointer",
21100
+ color: inputAreaConfig.text_color || "#111827",
21101
+ fontSize: "16px"
21102
+ }
21103
+ },
21104
+ "✕"
21105
+ )), selectedFilePreviewUrl ? /* @__PURE__ */ React.createElement(
21106
+ "div",
21107
+ {
21108
+ className: "overflow-hidden rounded-xl border mb-2 shadow-sm",
21109
+ style: {
21110
+ borderColor: inputAreaConfig.border_color || "#D1D5DB",
21111
+ width: "100%",
21112
+ maxWidth: 180,
21113
+ minWidth: 64,
21114
+ minHeight: 48,
21115
+ maxHeight: 160,
21116
+ borderRadius: 18,
21117
+ background: "#f3f4f6",
21118
+ display: "flex",
21119
+ alignItems: "center",
21120
+ justifyContent: "center"
21121
+ }
21122
+ },
21123
+ /* @__PURE__ */ React.createElement(
21124
+ "img",
21125
+ {
21126
+ src: selectedFilePreviewUrl,
21127
+ alt: selectedFile.name,
21128
+ style: {
21129
+ width: "100%",
21130
+ height: "auto",
21131
+ maxHeight: 160,
21132
+ objectFit: "cover",
21133
+ borderRadius: 18,
21134
+ aspectRatio: "4/3",
21135
+ background: "#f3f4f6",
21136
+ display: "block"
21137
+ }
21138
+ }
21139
+ )
21140
+ ) : /* @__PURE__ */ React.createElement(
21141
+ "div",
21142
+ {
21143
+ className: "rounded-xl border mb-2 flex items-center justify-center",
21144
+ style: {
21145
+ borderColor: inputAreaConfig.border_color || "#D1D5DB",
21146
+ width: 120,
21147
+ height: 64,
21148
+ color: inputAreaConfig.text_color || "#111827",
21149
+ background: "#f3f4f6",
21150
+ borderRadius: 16
21151
+ }
21152
+ },
21153
+ /* @__PURE__ */ React.createElement(ImAttachment, { style: { fontSize: 28, color: "#6b7280" } })
21154
+ ), /* @__PURE__ */ React.createElement("div", { style: { color: inputAreaConfig.text_color || "#111827", fontSize: "12px", opacity: 0.75, marginBottom: "8px", wordBreak: "break-all" } }, selectedFile.name)),
20812
21155
  showContactFields && /* @__PURE__ */ React.createElement("div", { className: "p-3 border rounded-lg mt-2", style: {
20813
- borderColor: showContactErrors && (!contactName.trim() || !contactEmail.trim() || !isValidEmail(contactEmail)) || emailIsInvalid ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
21156
+ borderColor: showContactErrors && (!contactName.trim() || !contactEmail.trim()) ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20814
21157
  backgroundColor: inputAreaConfig.background_color || "#FFF"
20815
21158
  } }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold mb-2", style: { color: inputAreaConfig.text_color || "#111827" } }, "How should we reach you?"), /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(
20816
21159
  "input",
@@ -20842,13 +21185,13 @@
20842
21185
  },
20843
21186
  className: "w-full px-3 py-1.5 border rounded-md",
20844
21187
  style: {
20845
- borderColor: showContactErrors && !contactEmail.trim() || emailIsInvalid ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
21188
+ borderColor: showContactErrors && !contactEmail.trim() ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20846
21189
  color: inputAreaConfig.text_color || "#111827",
20847
21190
  fontSize: "13px",
20848
21191
  backgroundColor: inputAreaConfig.background_color || "#FFF"
20849
21192
  }
20850
21193
  }
20851
- ), showContactErrors && (!contactName.trim() || !contactEmail.trim() || !isValidEmail(contactEmail)) && /* @__PURE__ */ React.createElement("div", { className: "text-xs", style: { color: "#EF4444" } }, `${!contactName.trim() ? "Name is required. " : ""}${!contactEmail.trim() ? "Email is required. " : ""}${contactEmail.trim() && !isValidEmail(contactEmail) ? "Enter a valid email address." : ""}`)))
21194
+ ), showContactErrors && (!contactName.trim() || !contactEmail.trim()) && /* @__PURE__ */ React.createElement("div", { className: "text-xs", style: { color: "#EF4444" } }, `${!contactName.trim() ? "Name is required. " : ""}${!contactEmail.trim() ? "Email is required." : ""}`)))
20852
21195
  ),
20853
21196
  inputAreaConfig.show_branding !== false && /* @__PURE__ */ React.createElement(
20854
21197
  "div",
@@ -20874,7 +21217,116 @@
20874
21217
  "Powered by WidgetKraft"
20875
21218
  )
20876
21219
  )
21220
+ ), previewImage && /* @__PURE__ */ React.createElement(
21221
+ "div",
21222
+ {
21223
+ onClick: () => setPreviewImage(null),
21224
+ style: {
21225
+ position: "fixed",
21226
+ inset: 0,
21227
+ backgroundColor: "rgba(0, 0, 0, 0.92)",
21228
+ zIndex: 1e4,
21229
+ cursor: "pointer",
21230
+ display: "flex",
21231
+ flexDirection: "column",
21232
+ alignItems: "center",
21233
+ justifyContent: "center",
21234
+ minHeight: "100vh",
21235
+ minWidth: "100vw"
21236
+ }
21237
+ },
21238
+ /* @__PURE__ */ React.createElement(
21239
+ "button",
21240
+ {
21241
+ onClick: (e) => {
21242
+ e.stopPropagation();
21243
+ setPreviewImage(null);
21244
+ },
21245
+ style: {
21246
+ position: "fixed",
21247
+ top: 24,
21248
+ right: 32,
21249
+ background: "rgba(0,0,0,0.7)",
21250
+ color: "white",
21251
+ border: "none",
21252
+ borderRadius: "50%",
21253
+ width: 44,
21254
+ height: 44,
21255
+ fontSize: 28,
21256
+ cursor: "pointer",
21257
+ display: "flex",
21258
+ alignItems: "center",
21259
+ justifyContent: "center",
21260
+ zIndex: 10001,
21261
+ boxShadow: "0 2px 8px 0 rgba(0,0,0,0.18)",
21262
+ transition: "background 0.2s"
21263
+ },
21264
+ "aria-label": "Close preview"
21265
+ },
21266
+ "✕"
21267
+ ),
21268
+ /* @__PURE__ */ React.createElement(
21269
+ "div",
21270
+ {
21271
+ onClick: (e) => e.stopPropagation(),
21272
+ style: {
21273
+ position: "relative",
21274
+ maxWidth: "90vw",
21275
+ maxHeight: "90vh",
21276
+ width: "auto",
21277
+ height: "auto",
21278
+ display: "flex",
21279
+ alignItems: "center",
21280
+ justifyContent: "center",
21281
+ borderRadius: 16,
21282
+ overflow: "hidden",
21283
+ background: "#18181b",
21284
+ boxShadow: "0 4px 32px 0 rgba(0,0,0,0.25)"
21285
+ }
21286
+ },
21287
+ /* @__PURE__ */ React.createElement(
21288
+ "img",
21289
+ {
21290
+ src: previewImage,
21291
+ alt: "Preview",
21292
+ style: {
21293
+ maxWidth: "90vw",
21294
+ maxHeight: "90vh",
21295
+ objectFit: "contain",
21296
+ borderRadius: 16,
21297
+ background: "#222",
21298
+ display: "block",
21299
+ margin: 0
21300
+ }
21301
+ }
21302
+ )
21303
+ )
20877
21304
  ), /* @__PURE__ */ React.createElement("style", { jsx: true }, `
21305
+ .custom-chat-textarea {
21306
+ transition: min-height 0.2s;
21307
+ }
21308
+ .custom-chat-textarea::-webkit-scrollbar {
21309
+ width: 6px;
21310
+ }
21311
+ .custom-chat-textarea::-webkit-scrollbar-thumb {
21312
+ background: #e5e7eb;
21313
+ border-radius: 3px;
21314
+ }
21315
+ .custom-chat-textarea:focus {
21316
+ outline: none;
21317
+ }
21318
+ // Auto-grow textarea height up to 4 rows
21319
+ useEffect(() => {
21320
+ if (!inputRef.current) return;
21321
+ const textarea = inputRef.current;
21322
+ textarea.style.height = 'auto';
21323
+ const maxHeight = 120; // ~4 rows
21324
+ textarea.style.height = Math.min(textarea.scrollHeight, maxHeight) + 'px';
21325
+ }, [inputMessage]);
21326
+ @keyframes spin {
21327
+ to { transform: rotate(360deg); }
21328
+ }
21329
+
20878
21330
  /* Hide scrollbar for all browsers */
20879
21331
  .hide-scrollbar::-webkit-scrollbar { display: none; }
20880
21332
  .hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getwidgets/live-chat-widget",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "main": "dist/live-chat-widget.umd.js",
5
5
  "unpkg": "dist/live-chat-widget.umd.js",
6
6
  "type": "module",