@getwidgets/live-chat-widget 1.0.2 → 1.0.4

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.
@@ -13744,8 +13744,9 @@
13744
13744
  function code$2(state, node2) {
13745
13745
  const value = node2.value ? node2.value + "\n" : "";
13746
13746
  const properties = {};
13747
- if (node2.lang) {
13748
- properties.className = ["language-" + node2.lang];
13747
+ const language = node2.lang ? node2.lang.split(/\s+/) : [];
13748
+ if (language.length > 0) {
13749
+ properties.className = ["language-" + language[0]];
13749
13750
  }
13750
13751
  let result = {
13751
13752
  type: "element",
@@ -19581,6 +19582,8 @@
19581
19582
  const [isLoading, setIsLoading] = reactExports.useState(false);
19582
19583
  const [isOpen, setIsOpen] = reactExports.useState(false);
19583
19584
  const [showNotificationPreview, setShowNotificationPreview] = reactExports.useState(true);
19585
+ const [unreadCount, setUnreadCount] = reactExports.useState(0);
19586
+ const [latestMessage, setLatestMessage] = reactExports.useState(null);
19584
19587
  const chatContainerRef = reactExports.useRef(null);
19585
19588
  const inputRef = reactExports.useRef(null);
19586
19589
  const [sessionId, setSessionId] = reactExports.useState(null);
@@ -19591,8 +19594,10 @@
19591
19594
  const [queuedFirstMessage, setQueuedFirstMessage] = reactExports.useState(null);
19592
19595
  const wsRef = reactExports.useRef(null);
19593
19596
  const audioRef = reactExports.useRef(null);
19597
+ const notificationAudioRef = reactExports.useRef(null);
19594
19598
  const [hasCheckedSession, setHasCheckedSession] = reactExports.useState(false);
19595
19599
  const [windowWidth, setWindowWidth] = reactExports.useState(typeof window !== "undefined" ? window.innerWidth : 1024);
19600
+ const isOpenRef = reactExports.useRef(false);
19596
19601
  const normalizeIncomingMessage = (m2) => {
19597
19602
  if (!m2) return { id: void 0, role: "agent", content: "", timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), created_at: Date.now() };
19598
19603
  if (m2.sent_by) {
@@ -19627,7 +19632,11 @@
19627
19632
  }, []);
19628
19633
  reactExports.useEffect(() => {
19629
19634
  if (chatContainerRef.current) {
19630
- chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
19635
+ setTimeout(() => {
19636
+ if (chatContainerRef.current) {
19637
+ chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
19638
+ }
19639
+ }, 100);
19631
19640
  }
19632
19641
  }, [messages, localMessages]);
19633
19642
  reactExports.useEffect(() => {
@@ -19686,6 +19695,12 @@
19686
19695
  } catch (e) {
19687
19696
  console.warn("Failed to init audioRef", e);
19688
19697
  }
19698
+ try {
19699
+ notificationAudioRef.current = new Audio("https://cdn.widgetkraft.com/mixkit-bubble-pop-up-alert-notification-2357.mp3");
19700
+ notificationAudioRef.current.preload = "auto";
19701
+ } catch (e) {
19702
+ console.warn("Failed to init notificationAudioRef", e);
19703
+ }
19689
19704
  }, []);
19690
19705
  const startLiveSession = async (widgetUuid, currentSessionId) => {
19691
19706
  try {
@@ -19733,11 +19748,39 @@
19733
19748
  if (Array.isArray(data)) {
19734
19749
  const list2 = data.map(normalizeIncomingMessage);
19735
19750
  setLocalMessages(list2);
19751
+ if (!isOpenRef.current) {
19752
+ const agentMessages = list2.filter((m22) => m22.role === "agent");
19753
+ if (agentMessages.length > 0) {
19754
+ setUnreadCount((prev) => prev + agentMessages.length);
19755
+ setLatestMessage(agentMessages[agentMessages.length - 1]);
19756
+ if (notificationAudioRef.current) {
19757
+ try {
19758
+ notificationAudioRef.current.play().catch(() => {
19759
+ });
19760
+ } catch (e) {
19761
+ }
19762
+ }
19763
+ }
19764
+ }
19736
19765
  return;
19737
19766
  }
19738
19767
  if ((data == null ? void 0 : data.messages) && Array.isArray(data.messages)) {
19739
19768
  const list2 = data.messages.map(normalizeIncomingMessage);
19740
19769
  setLocalMessages(list2);
19770
+ if (!isOpenRef.current) {
19771
+ const agentMessages = list2.filter((m22) => m22.role === "agent");
19772
+ if (agentMessages.length > 0) {
19773
+ setUnreadCount((prev) => prev + agentMessages.length);
19774
+ setLatestMessage(agentMessages[agentMessages.length - 1]);
19775
+ if (notificationAudioRef.current) {
19776
+ try {
19777
+ notificationAudioRef.current.play().catch(() => {
19778
+ });
19779
+ } catch (e) {
19780
+ }
19781
+ }
19782
+ }
19783
+ }
19741
19784
  return;
19742
19785
  }
19743
19786
  const m2 = normalizeIncomingMessage(data);
@@ -19754,16 +19797,38 @@
19754
19797
  }
19755
19798
  return [...prev, m2];
19756
19799
  });
19757
- if (!replaced && audioRef.current) {
19758
- try {
19759
- audioRef.current.play().catch(() => {
19760
- });
19761
- } catch (e) {
19800
+ if (!replaced) {
19801
+ if (!isOpenRef.current && m2.role === "agent") {
19802
+ setUnreadCount((prev) => prev + 1);
19803
+ setLatestMessage(m2);
19804
+ if (notificationAudioRef.current) {
19805
+ try {
19806
+ notificationAudioRef.current.play().catch(() => {
19807
+ });
19808
+ } catch (e) {
19809
+ }
19810
+ }
19811
+ } else if (audioRef.current) {
19812
+ try {
19813
+ audioRef.current.play().catch(() => {
19814
+ });
19815
+ } catch (e) {
19816
+ }
19762
19817
  }
19763
19818
  }
19764
19819
  } else {
19765
19820
  setLocalMessages((prev) => [...prev, m2]);
19766
- if (audioRef.current) {
19821
+ if (!isOpenRef.current && m2.role === "agent") {
19822
+ setUnreadCount((prev) => prev + 1);
19823
+ setLatestMessage(m2);
19824
+ if (notificationAudioRef.current) {
19825
+ try {
19826
+ notificationAudioRef.current.play().catch(() => {
19827
+ });
19828
+ } catch (e) {
19829
+ }
19830
+ }
19831
+ } else if (audioRef.current) {
19767
19832
  try {
19768
19833
  audioRef.current.play().catch(() => {
19769
19834
  });
@@ -19889,7 +19954,15 @@
19889
19954
  }
19890
19955
  };
19891
19956
  const toggleWidget = () => {
19892
- setIsOpen((v2) => !v2);
19957
+ setIsOpen((v2) => {
19958
+ const newState = !v2;
19959
+ isOpenRef.current = newState;
19960
+ if (newState) {
19961
+ setUnreadCount(0);
19962
+ setLatestMessage(null);
19963
+ }
19964
+ return newState;
19965
+ });
19893
19966
  };
19894
19967
  const handleCloseNotification = (e) => {
19895
19968
  e.stopPropagation();
@@ -19959,11 +20032,11 @@
19959
20032
  "button",
19960
20033
  {
19961
20034
  onClick: toggleWidget,
19962
- className: "rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 flex flex-col gap-2 p-4 max-w-xs responsive-notification",
20035
+ className: "rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 flex flex-col gap-2 p-4 max-w-xs responsive-notification relative",
19963
20036
  style: {
19964
20037
  backgroundColor: headerConfig.background_color || "#111827",
19965
20038
  color: "#FFFFFF",
19966
- border: "none",
20039
+ border: unreadCount > 0 ? "3px solid #EF4444" : "none",
19967
20040
  cursor: "pointer",
19968
20041
  textAlign: "left",
19969
20042
  minWidth: isMobile ? "90vw" : "280px",
@@ -19971,6 +20044,22 @@
19971
20044
  margin: isMobile ? "0 auto" : "0"
19972
20045
  }
19973
20046
  },
20047
+ unreadCount > 0 && /* @__PURE__ */ React.createElement(
20048
+ "div",
20049
+ {
20050
+ className: "absolute -top-2 -right-2 bg-red-500 text-white text-xs font-bold rounded-full w-6 h-6 flex items-center justify-center",
20051
+ style: {
20052
+ backgroundColor: "#EF4444",
20053
+ color: "#FFFFFF",
20054
+ fontSize: "12px",
20055
+ fontWeight: "bold",
20056
+ minWidth: "24px",
20057
+ minHeight: "24px",
20058
+ zIndex: 10
20059
+ }
20060
+ },
20061
+ unreadCount > 99 ? "99+" : unreadCount
20062
+ ),
19974
20063
  /* @__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(
19975
20064
  "img",
19976
20065
  {
@@ -20016,18 +20105,18 @@
20016
20105
  },
20017
20106
  "×"
20018
20107
  )),
20019
- /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-relaxed text-gray-200", style: { fontSize: isMobile ? "13px" : "14px" } }, (inputAreaConfig == null ? void 0 : inputAreaConfig.first_ai_message) || headerConfig.subtitle || "Connect with visitors")
20108
+ /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-relaxed text-gray-200", style: { fontSize: isMobile ? "13px" : "14px" } }, (latestMessage == null ? void 0 : latestMessage.content) || (inputAreaConfig == null ? void 0 : inputAreaConfig.first_ai_message) || headerConfig.subtitle || "Connect with visitors")
20020
20109
  ), !isOpen && !showNotificationPreview && /* @__PURE__ */ React.createElement(
20021
20110
  "button",
20022
20111
  {
20023
20112
  onClick: toggleWidget,
20024
- className: "rounded-full shadow-lg hover:shadow-xl transition-all duration-300 flex items-center justify-center",
20113
+ className: "rounded-full shadow-lg hover:shadow-xl transition-all duration-300 flex items-center justify-center relative",
20025
20114
  style: {
20026
20115
  backgroundColor: headerConfig.background_color || "#111827",
20027
20116
  color: headerConfig.font_color || "#FFFFFF",
20028
20117
  width: isMobile ? "52px" : "56px",
20029
20118
  height: isMobile ? "52px" : "56px",
20030
- border: "none",
20119
+ border: unreadCount > 0 ? "3px solid #EF4444" : "none",
20031
20120
  cursor: "pointer",
20032
20121
  margin: isMobile ? "16px" : "0"
20033
20122
  }
@@ -20042,7 +20131,22 @@
20042
20131
  height: isMobile ? 24 : 28
20043
20132
  }
20044
20133
  }
20045
- ) : /* @__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" }))
20134
+ ) : /* @__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" })),
20135
+ unreadCount > 0 && /* @__PURE__ */ React.createElement(
20136
+ "div",
20137
+ {
20138
+ className: "absolute -top-2 -right-2 bg-red-500 text-white text-xs font-bold rounded-full w-6 h-6 flex items-center justify-center",
20139
+ style: {
20140
+ backgroundColor: "#EF4444",
20141
+ color: "#FFFFFF",
20142
+ fontSize: "12px",
20143
+ fontWeight: "bold",
20144
+ minWidth: "24px",
20145
+ minHeight: "24px"
20146
+ }
20147
+ },
20148
+ unreadCount > 99 ? "99+" : unreadCount
20149
+ )
20046
20150
  ), isOpen && /* @__PURE__ */ React.createElement(
20047
20151
  "div",
20048
20152
  {
@@ -20101,13 +20205,13 @@
20101
20205
  borderRadius: "4px"
20102
20206
  }
20103
20207
  }
20104
- ), /* @__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"))
20208
+ ), !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"))
20105
20209
  ),
20106
20210
  /* @__PURE__ */ React.createElement(
20107
20211
  "div",
20108
20212
  {
20109
20213
  ref: chatContainerRef,
20110
- className: "flex-1 relative overflow-y-auto",
20214
+ className: "flex-1 relative overflow-y-auto chat-scrollable",
20111
20215
  style: {
20112
20216
  backgroundColor: appearanceConfig.background_color || "#F9FAFB",
20113
20217
  paddingBottom: isMobile ? "8px" : "0"
@@ -20316,21 +20420,27 @@
20316
20420
  }
20317
20421
 
20318
20422
  /* Scrollbar styling for chat container */
20319
- .responsive-messages-area::-webkit-scrollbar {
20320
- width: 6px;
20423
+ .chat-scrollable::-webkit-scrollbar {
20424
+ width: 4px;
20321
20425
  }
20322
20426
 
20323
- .responsive-messages-area::-webkit-scrollbar-track {
20427
+ .chat-scrollable::-webkit-scrollbar-track {
20324
20428
  background: transparent;
20325
20429
  }
20326
20430
 
20327
- .responsive-messages-area::-webkit-scrollbar-thumb {
20328
- background: #ccc;
20329
- border-radius: 3px;
20431
+ .chat-scrollable::-webkit-scrollbar-thumb {
20432
+ background: rgba(0, 0, 0, 0.2);
20433
+ border-radius: 2px;
20434
+ }
20435
+
20436
+ .chat-scrollable::-webkit-scrollbar-thumb:hover {
20437
+ background: rgba(0, 0, 0, 0.3);
20330
20438
  }
20331
20439
 
20332
- .responsive-messages-area::-webkit-scrollbar-thumb:hover {
20333
- background: #999;
20440
+ /* Firefox scrollbar */
20441
+ .chat-scrollable {
20442
+ scrollbar-width: thin;
20443
+ scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
20334
20444
  }
20335
20445
  `));
20336
20446
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getwidgets/live-chat-widget",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "main": "dist/live-chat-widget.umd.js",
5
5
  "unpkg": "dist/live-chat-widget.umd.js",
6
6
  "type": "module",