@getwidgets/live-chat-widget 1.0.0 → 1.0.2
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/dist/live-chat-widget.umd.js +524 -66
- package/package.json +3 -1
|
@@ -19540,6 +19540,21 @@
|
|
|
19540
19540
|
if (!res.ok) throw new Error("Failed to fetch widget config");
|
|
19541
19541
|
return res.json();
|
|
19542
19542
|
};
|
|
19543
|
+
const checkSessionStatus = async (widgetId, sessionId) => {
|
|
19544
|
+
try {
|
|
19545
|
+
const url = `https://api.getwidgets.app/api/widgets/livechat/${widgetId}/status/?session_id=${sessionId}`;
|
|
19546
|
+
const res = await fetch(url);
|
|
19547
|
+
if (!res.ok) {
|
|
19548
|
+
const txt = await res.text().catch(() => null);
|
|
19549
|
+
throw new Error(txt || "Failed to check session status");
|
|
19550
|
+
}
|
|
19551
|
+
const data = await res.json();
|
|
19552
|
+
return data;
|
|
19553
|
+
} catch (e) {
|
|
19554
|
+
console.warn("checkSessionStatus failed", e);
|
|
19555
|
+
return { error: "Failed to check session status" };
|
|
19556
|
+
}
|
|
19557
|
+
};
|
|
19543
19558
|
const getSessionId = (widgetId) => {
|
|
19544
19559
|
if (typeof window === "undefined") return null;
|
|
19545
19560
|
const key = `live-chat-widget/session-id`;
|
|
@@ -19565,13 +19580,19 @@
|
|
|
19565
19580
|
const [inputMessage, setInputMessage] = reactExports.useState("");
|
|
19566
19581
|
const [isLoading, setIsLoading] = reactExports.useState(false);
|
|
19567
19582
|
const [isOpen, setIsOpen] = reactExports.useState(false);
|
|
19583
|
+
const [showNotificationPreview, setShowNotificationPreview] = reactExports.useState(true);
|
|
19568
19584
|
const chatContainerRef = reactExports.useRef(null);
|
|
19569
19585
|
const inputRef = reactExports.useRef(null);
|
|
19570
19586
|
const [sessionId, setSessionId] = reactExports.useState(null);
|
|
19571
19587
|
const [localMessages, setLocalMessages] = reactExports.useState([]);
|
|
19572
19588
|
const [localLoading, setLocalLoading] = reactExports.useState(false);
|
|
19589
|
+
const [isSessionInitialized, setIsSessionInitialized] = reactExports.useState(false);
|
|
19590
|
+
const [isConnecting, setIsConnecting] = reactExports.useState(false);
|
|
19591
|
+
const [queuedFirstMessage, setQueuedFirstMessage] = reactExports.useState(null);
|
|
19573
19592
|
const wsRef = reactExports.useRef(null);
|
|
19574
19593
|
const audioRef = reactExports.useRef(null);
|
|
19594
|
+
const [hasCheckedSession, setHasCheckedSession] = reactExports.useState(false);
|
|
19595
|
+
const [windowWidth, setWindowWidth] = reactExports.useState(typeof window !== "undefined" ? window.innerWidth : 1024);
|
|
19575
19596
|
const normalizeIncomingMessage = (m2) => {
|
|
19576
19597
|
if (!m2) return { id: void 0, role: "agent", content: "", timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), created_at: Date.now() };
|
|
19577
19598
|
if (m2.sent_by) {
|
|
@@ -19597,6 +19618,13 @@
|
|
|
19597
19618
|
created_at: ts
|
|
19598
19619
|
};
|
|
19599
19620
|
};
|
|
19621
|
+
reactExports.useEffect(() => {
|
|
19622
|
+
const handleResize = () => {
|
|
19623
|
+
setWindowWidth(window.innerWidth);
|
|
19624
|
+
};
|
|
19625
|
+
window.addEventListener("resize", handleResize);
|
|
19626
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
19627
|
+
}, []);
|
|
19600
19628
|
reactExports.useEffect(() => {
|
|
19601
19629
|
if (chatContainerRef.current) {
|
|
19602
19630
|
chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
|
|
@@ -19606,38 +19634,51 @@
|
|
|
19606
19634
|
const id2 = getSessionId();
|
|
19607
19635
|
setSessionId(id2);
|
|
19608
19636
|
}, [widgetId]);
|
|
19637
|
+
reactExports.useEffect(() => {
|
|
19638
|
+
const dismissalKey = `live-chat-notification-dismissed/${widgetId}`;
|
|
19639
|
+
const dismissalTime = localStorage.getItem(dismissalKey);
|
|
19640
|
+
if (dismissalTime) {
|
|
19641
|
+
const dismissedAt = parseInt(dismissalTime);
|
|
19642
|
+
const now = Date.now();
|
|
19643
|
+
const timeSinceDismissal = now - dismissedAt;
|
|
19644
|
+
const oneDayInMs = 24 * 60 * 60 * 1e3;
|
|
19645
|
+
if (timeSinceDismissal < oneDayInMs) {
|
|
19646
|
+
setShowNotificationPreview(false);
|
|
19647
|
+
return;
|
|
19648
|
+
} else {
|
|
19649
|
+
localStorage.removeItem(dismissalKey);
|
|
19650
|
+
setShowNotificationPreview(true);
|
|
19651
|
+
}
|
|
19652
|
+
} else {
|
|
19653
|
+
setShowNotificationPreview(true);
|
|
19654
|
+
}
|
|
19655
|
+
}, [widgetId]);
|
|
19609
19656
|
reactExports.useEffect(() => {
|
|
19610
19657
|
if (!widgetId) return;
|
|
19611
19658
|
fetchWidgetConfig(widgetId).then((data) => setConfig(data)).catch((err) => console.error("Failed to load widget config:", err));
|
|
19612
19659
|
}, [widgetId, apiKey]);
|
|
19613
19660
|
reactExports.useEffect(() => {
|
|
19614
|
-
|
|
19615
|
-
|
|
19616
|
-
|
|
19617
|
-
const existing = getSessionId();
|
|
19618
|
-
try {
|
|
19619
|
-
localStorage.setItem("live-chat-widget/session-id", existing);
|
|
19620
|
-
} catch (e) {
|
|
19621
|
-
}
|
|
19622
|
-
const sid = await startLiveSession(widgetId, existing);
|
|
19623
|
-
if (!mounted) return;
|
|
19661
|
+
const checkAndInitializeSession = async () => {
|
|
19662
|
+
var _a2;
|
|
19663
|
+
if (!widgetId || !sessionId || hasCheckedSession) return;
|
|
19624
19664
|
try {
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19637
|
-
|
|
19665
|
+
const status = await checkSessionStatus(widgetId, sessionId);
|
|
19666
|
+
if (status.success && ((_a2 = status.data) == null ? void 0 : _a2.is_active)) {
|
|
19667
|
+
console.log("Session is active, connecting to websocket...");
|
|
19668
|
+
setIsConnecting(true);
|
|
19669
|
+
openLiveWebsocket(sessionId);
|
|
19670
|
+
setIsSessionInitialized(true);
|
|
19671
|
+
} else {
|
|
19672
|
+
console.log("Session not active, will create new session on first message");
|
|
19673
|
+
}
|
|
19674
|
+
} catch (error) {
|
|
19675
|
+
console.error("Failed to check session status:", error);
|
|
19676
|
+
} finally {
|
|
19677
|
+
setHasCheckedSession(true);
|
|
19638
19678
|
}
|
|
19639
19679
|
};
|
|
19640
|
-
|
|
19680
|
+
checkAndInitializeSession();
|
|
19681
|
+
}, [widgetId, sessionId, hasCheckedSession]);
|
|
19641
19682
|
reactExports.useEffect(() => {
|
|
19642
19683
|
try {
|
|
19643
19684
|
audioRef.current = new Audio("https://res.cloudinary.com/dtqjv8s9r/video/upload/v1699024420/new-notification-014-363678_iatlfa.mp3");
|
|
@@ -19662,10 +19703,10 @@
|
|
|
19662
19703
|
return data.session_id || data.id || currentSessionId;
|
|
19663
19704
|
} catch (e) {
|
|
19664
19705
|
console.warn("startLiveSession failed", e);
|
|
19665
|
-
|
|
19706
|
+
throw e;
|
|
19666
19707
|
}
|
|
19667
19708
|
};
|
|
19668
|
-
const openLiveWebsocket = (sid) => {
|
|
19709
|
+
const openLiveWebsocket = (sid, onOpenCallback) => {
|
|
19669
19710
|
if (!sid) return;
|
|
19670
19711
|
try {
|
|
19671
19712
|
if (wsRef.current) {
|
|
@@ -19681,6 +19722,10 @@
|
|
|
19681
19722
|
ws.onopen = () => {
|
|
19682
19723
|
console.log("LiveChat WS connected", url);
|
|
19683
19724
|
setLocalLoading(false);
|
|
19725
|
+
setIsConnecting(false);
|
|
19726
|
+
if (onOpenCallback) {
|
|
19727
|
+
onOpenCallback();
|
|
19728
|
+
}
|
|
19684
19729
|
};
|
|
19685
19730
|
ws.onmessage = (evt) => {
|
|
19686
19731
|
try {
|
|
@@ -19737,10 +19782,68 @@
|
|
|
19737
19782
|
} catch (e) {
|
|
19738
19783
|
}
|
|
19739
19784
|
wsRef.current = null;
|
|
19785
|
+
setIsSessionInitialized(false);
|
|
19786
|
+
setIsConnecting(false);
|
|
19787
|
+
};
|
|
19788
|
+
ws.onerror = (e) => {
|
|
19789
|
+
console.error("LiveChat WS error", e);
|
|
19790
|
+
setIsConnecting(false);
|
|
19740
19791
|
};
|
|
19741
|
-
ws.onerror = (e) => console.error("LiveChat WS error", e);
|
|
19742
19792
|
} catch (e) {
|
|
19743
19793
|
console.error("Failed to open LiveChat WS", e);
|
|
19794
|
+
setIsConnecting(false);
|
|
19795
|
+
}
|
|
19796
|
+
};
|
|
19797
|
+
const sendMessageOverWebSocket = (message) => {
|
|
19798
|
+
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
|
19799
|
+
try {
|
|
19800
|
+
wsRef.current.send(JSON.stringify({ message }));
|
|
19801
|
+
return true;
|
|
19802
|
+
} catch (e) {
|
|
19803
|
+
console.error("Failed to send live message", e);
|
|
19804
|
+
return false;
|
|
19805
|
+
}
|
|
19806
|
+
}
|
|
19807
|
+
return false;
|
|
19808
|
+
};
|
|
19809
|
+
const initializeSession = async (firstMessage) => {
|
|
19810
|
+
if (isSessionInitialized) return true;
|
|
19811
|
+
setIsConnecting(true);
|
|
19812
|
+
setLocalLoading(true);
|
|
19813
|
+
try {
|
|
19814
|
+
const newSessionId = v4();
|
|
19815
|
+
const sid = await startLiveSession(widgetId, newSessionId);
|
|
19816
|
+
try {
|
|
19817
|
+
localStorage.setItem("live-chat-widget/session-id", sid);
|
|
19818
|
+
} catch (e) {
|
|
19819
|
+
}
|
|
19820
|
+
setSessionId(sid);
|
|
19821
|
+
setIsSessionInitialized(true);
|
|
19822
|
+
openLiveWebsocket(sid, () => {
|
|
19823
|
+
if (firstMessage) {
|
|
19824
|
+
const sent = sendMessageOverWebSocket(firstMessage);
|
|
19825
|
+
if (!sent) {
|
|
19826
|
+
setLocalMessages((prev) => [...prev, {
|
|
19827
|
+
id: `error-${Date.now()}`,
|
|
19828
|
+
role: "system",
|
|
19829
|
+
content: "Failed to send message. Please try again.",
|
|
19830
|
+
isError: true
|
|
19831
|
+
}]);
|
|
19832
|
+
}
|
|
19833
|
+
}
|
|
19834
|
+
});
|
|
19835
|
+
return true;
|
|
19836
|
+
} catch (error) {
|
|
19837
|
+
console.error("Failed to initialize session:", error);
|
|
19838
|
+
setLocalLoading(false);
|
|
19839
|
+
setIsConnecting(false);
|
|
19840
|
+
setLocalMessages((prev) => [...prev, {
|
|
19841
|
+
id: `error-${Date.now()}`,
|
|
19842
|
+
role: "system",
|
|
19843
|
+
content: "Failed to connect to support. Please try again.",
|
|
19844
|
+
isError: true
|
|
19845
|
+
}]);
|
|
19846
|
+
return false;
|
|
19744
19847
|
}
|
|
19745
19848
|
};
|
|
19746
19849
|
const performSend = async () => {
|
|
@@ -19755,19 +19858,28 @@
|
|
|
19755
19858
|
pending: true
|
|
19756
19859
|
};
|
|
19757
19860
|
setLocalMessages((prev) => [...prev, userMsg]);
|
|
19758
|
-
setLocalLoading(false);
|
|
19759
19861
|
setInputMessage("");
|
|
19760
|
-
if (
|
|
19761
|
-
|
|
19762
|
-
|
|
19763
|
-
|
|
19764
|
-
|
|
19862
|
+
if (!isSessionInitialized) {
|
|
19863
|
+
setLocalMessages((prev) => [...prev, {
|
|
19864
|
+
id: `sys-${Date.now()}`,
|
|
19865
|
+
role: "system",
|
|
19866
|
+
content: "Connecting to support..."
|
|
19867
|
+
}]);
|
|
19868
|
+
const initialized = await initializeSession(msg);
|
|
19869
|
+
if (!initialized) {
|
|
19870
|
+
setLocalMessages((prev) => prev.filter((m2) => m2.role !== "system" || !m2.content.includes("Connecting")));
|
|
19765
19871
|
}
|
|
19766
19872
|
} else {
|
|
19767
|
-
|
|
19768
|
-
|
|
19769
|
-
|
|
19770
|
-
|
|
19873
|
+
const sent = sendMessageOverWebSocket(msg);
|
|
19874
|
+
if (!sent) {
|
|
19875
|
+
setTimeout(() => {
|
|
19876
|
+
setLocalMessages((prev) => [...prev, {
|
|
19877
|
+
id: `sys-${Date.now()}`,
|
|
19878
|
+
role: "system",
|
|
19879
|
+
content: "Reconnecting..."
|
|
19880
|
+
}]);
|
|
19881
|
+
}, 300);
|
|
19882
|
+
}
|
|
19771
19883
|
}
|
|
19772
19884
|
};
|
|
19773
19885
|
const handleKeyPress = (e) => {
|
|
@@ -19776,7 +19888,15 @@
|
|
|
19776
19888
|
performSend();
|
|
19777
19889
|
}
|
|
19778
19890
|
};
|
|
19779
|
-
const toggleWidget = () =>
|
|
19891
|
+
const toggleWidget = () => {
|
|
19892
|
+
setIsOpen((v2) => !v2);
|
|
19893
|
+
};
|
|
19894
|
+
const handleCloseNotification = (e) => {
|
|
19895
|
+
e.stopPropagation();
|
|
19896
|
+
const dismissalKey = `live-chat-notification-dismissed/${widgetId}`;
|
|
19897
|
+
localStorage.setItem(dismissalKey, Date.now().toString());
|
|
19898
|
+
setShowNotificationPreview(false);
|
|
19899
|
+
};
|
|
19780
19900
|
if (!config) return null;
|
|
19781
19901
|
const { header, chat_area, input_area, appearance } = ((_a = config.config) == null ? void 0 : _a.chat_widget) || {};
|
|
19782
19902
|
const headerConfig = header || {};
|
|
@@ -19784,13 +19904,17 @@
|
|
|
19784
19904
|
const chatAreaConfig = chat_area || {};
|
|
19785
19905
|
const inputAreaConfig = input_area || {};
|
|
19786
19906
|
const getResponsiveDimensions = () => {
|
|
19787
|
-
const
|
|
19788
|
-
if (
|
|
19907
|
+
const isMobile2 = windowWidth < 768;
|
|
19908
|
+
if (isMobile2) {
|
|
19789
19909
|
return {
|
|
19790
19910
|
width: "95vw",
|
|
19791
|
-
height: "
|
|
19911
|
+
height: "85vh",
|
|
19912
|
+
// Changed from 60vh to 85vh for better mobile experience
|
|
19792
19913
|
maxWidth: "95vw",
|
|
19793
|
-
maxHeight: "
|
|
19914
|
+
maxHeight: "95vh",
|
|
19915
|
+
bottom: "0",
|
|
19916
|
+
right: "0",
|
|
19917
|
+
borderRadius: "12px 12px 0 0"
|
|
19794
19918
|
};
|
|
19795
19919
|
}
|
|
19796
19920
|
return {
|
|
@@ -19802,7 +19926,11 @@
|
|
|
19802
19926
|
};
|
|
19803
19927
|
const getPositionStyles = () => {
|
|
19804
19928
|
const position2 = appearanceConfig.position || "bottom-right";
|
|
19929
|
+
const isMobile2 = windowWidth < 768;
|
|
19805
19930
|
const baseStyles = { position: "fixed", zIndex: 9999 };
|
|
19931
|
+
if (isMobile2) {
|
|
19932
|
+
return { ...baseStyles, left: 0, right: 0, bottom: 0 };
|
|
19933
|
+
}
|
|
19806
19934
|
const desktopSpacing = "20px";
|
|
19807
19935
|
switch (position2) {
|
|
19808
19936
|
case "bottom-left":
|
|
@@ -19818,6 +19946,7 @@
|
|
|
19818
19946
|
const responsiveDimensions = getResponsiveDimensions();
|
|
19819
19947
|
const combinedMessages = [...messages || [], ...localMessages];
|
|
19820
19948
|
const getContrastColor = (hex) => {
|
|
19949
|
+
if (!hex) return "#000";
|
|
19821
19950
|
const c = hex.replace("#", "");
|
|
19822
19951
|
const r2 = parseInt(c.substr(0, 2), 16);
|
|
19823
19952
|
const g = parseInt(c.substr(2, 2), 16);
|
|
@@ -19825,7 +19954,70 @@
|
|
|
19825
19954
|
const luminance = (0.299 * r2 + 0.587 * g + 0.114 * b) / 255;
|
|
19826
19955
|
return luminance > 0.5 ? "#000000" : "#FFFFFF";
|
|
19827
19956
|
};
|
|
19828
|
-
|
|
19957
|
+
const isMobile = windowWidth < 768;
|
|
19958
|
+
return /* @__PURE__ */ React.createElement("div", { style: getPositionStyles() }, !isOpen && showNotificationPreview && /* @__PURE__ */ React.createElement(
|
|
19959
|
+
"button",
|
|
19960
|
+
{
|
|
19961
|
+
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",
|
|
19963
|
+
style: {
|
|
19964
|
+
backgroundColor: headerConfig.background_color || "#111827",
|
|
19965
|
+
color: "#FFFFFF",
|
|
19966
|
+
border: "none",
|
|
19967
|
+
cursor: "pointer",
|
|
19968
|
+
textAlign: "left",
|
|
19969
|
+
minWidth: isMobile ? "90vw" : "280px",
|
|
19970
|
+
maxWidth: isMobile ? "90vw" : "400px",
|
|
19971
|
+
margin: isMobile ? "0 auto" : "0"
|
|
19972
|
+
}
|
|
19973
|
+
},
|
|
19974
|
+
/* @__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
|
+
"img",
|
|
19976
|
+
{
|
|
19977
|
+
src: headerConfig.logo_url,
|
|
19978
|
+
alt: "Chat",
|
|
19979
|
+
className: "responsive-notification-logo",
|
|
19980
|
+
style: {
|
|
19981
|
+
width: "32px",
|
|
19982
|
+
height: "32px",
|
|
19983
|
+
borderRadius: "50%",
|
|
19984
|
+
flexShrink: 0
|
|
19985
|
+
}
|
|
19986
|
+
}
|
|
19987
|
+
) : /* @__PURE__ */ React.createElement(
|
|
19988
|
+
"div",
|
|
19989
|
+
{
|
|
19990
|
+
className: "rounded-full flex items-center justify-center responsive-notification-icon",
|
|
19991
|
+
style: {
|
|
19992
|
+
width: "32px",
|
|
19993
|
+
height: "32px",
|
|
19994
|
+
backgroundColor: headerConfig.background_color || "#111827",
|
|
19995
|
+
flexShrink: 0
|
|
19996
|
+
}
|
|
19997
|
+
},
|
|
19998
|
+
/* @__PURE__ */ React.createElement(
|
|
19999
|
+
"svg",
|
|
20000
|
+
{
|
|
20001
|
+
viewBox: "0 0 24 24",
|
|
20002
|
+
fill: "currentColor",
|
|
20003
|
+
style: {
|
|
20004
|
+
width: "16px",
|
|
20005
|
+
height: "16px"
|
|
20006
|
+
}
|
|
20007
|
+
},
|
|
20008
|
+
/* @__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" })
|
|
20009
|
+
)
|
|
20010
|
+
), /* @__PURE__ */ React.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React.createElement("h3", { className: "font-semibold text-sm" }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "text-xs opacity-70" }, "Just now"))), /* @__PURE__ */ React.createElement(
|
|
20011
|
+
"button",
|
|
20012
|
+
{
|
|
20013
|
+
onClick: handleCloseNotification,
|
|
20014
|
+
className: "text-gray-400 hover:text-white transition-colors flex-shrink-0 responsive-notification-close",
|
|
20015
|
+
style: { fontSize: "20px", padding: "0", border: "none", background: "none", cursor: "pointer" }
|
|
20016
|
+
},
|
|
20017
|
+
"×"
|
|
20018
|
+
)),
|
|
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")
|
|
20020
|
+
), !isOpen && !showNotificationPreview && /* @__PURE__ */ React.createElement(
|
|
19829
20021
|
"button",
|
|
19830
20022
|
{
|
|
19831
20023
|
onClick: toggleWidget,
|
|
@@ -19833,48 +20025,314 @@
|
|
|
19833
20025
|
style: {
|
|
19834
20026
|
backgroundColor: headerConfig.background_color || "#111827",
|
|
19835
20027
|
color: headerConfig.font_color || "#FFFFFF",
|
|
19836
|
-
width: "56px",
|
|
19837
|
-
height: "56px",
|
|
20028
|
+
width: isMobile ? "52px" : "56px",
|
|
20029
|
+
height: isMobile ? "52px" : "56px",
|
|
19838
20030
|
border: "none",
|
|
19839
|
-
cursor: "pointer"
|
|
20031
|
+
cursor: "pointer",
|
|
20032
|
+
margin: isMobile ? "16px" : "0"
|
|
19840
20033
|
}
|
|
19841
20034
|
},
|
|
19842
|
-
headerConfig.logo_url ? /* @__PURE__ */ React.createElement(
|
|
20035
|
+
headerConfig.logo_url ? /* @__PURE__ */ React.createElement(
|
|
20036
|
+
"img",
|
|
20037
|
+
{
|
|
20038
|
+
src: headerConfig.logo_url,
|
|
20039
|
+
alt: "logo",
|
|
20040
|
+
style: {
|
|
20041
|
+
width: isMobile ? 24 : 28,
|
|
20042
|
+
height: isMobile ? 24 : 28
|
|
20043
|
+
}
|
|
20044
|
+
}
|
|
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" }))
|
|
19843
20046
|
), isOpen && /* @__PURE__ */ React.createElement(
|
|
19844
20047
|
"div",
|
|
19845
20048
|
{
|
|
19846
|
-
className: "rounded-xl
|
|
20049
|
+
className: "rounded-xl shadow-lg overflow-hidden transition-all duration-300 flex flex-col",
|
|
19847
20050
|
style: {
|
|
19848
20051
|
width: responsiveDimensions.width,
|
|
19849
20052
|
height: responsiveDimensions.height,
|
|
19850
20053
|
maxWidth: responsiveDimensions.maxWidth,
|
|
19851
20054
|
maxHeight: responsiveDimensions.maxHeight,
|
|
19852
20055
|
backgroundColor: appearanceConfig.background_color || "#F9FAFB",
|
|
19853
|
-
borderRadius: appearanceConfig.border_radius || "16px"
|
|
20056
|
+
borderRadius: isMobile ? responsiveDimensions.borderRadius : appearanceConfig.border_radius || "16px",
|
|
20057
|
+
...isMobile ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 } : {}
|
|
19854
20058
|
}
|
|
19855
20059
|
},
|
|
19856
20060
|
/* @__PURE__ */ React.createElement(
|
|
19857
20061
|
"div",
|
|
19858
20062
|
{
|
|
19859
|
-
className: "
|
|
19860
|
-
style: {
|
|
20063
|
+
className: "text-center flex-shrink-0 relative transition-colors duration-300",
|
|
20064
|
+
style: {
|
|
20065
|
+
backgroundColor: headerConfig.background_color || "#111827",
|
|
20066
|
+
color: headerConfig.font_color || "#FFFFFF",
|
|
20067
|
+
padding: isMobile ? "12px 16px" : "16px 20px",
|
|
20068
|
+
minHeight: "auto"
|
|
20069
|
+
}
|
|
20070
|
+
},
|
|
20071
|
+
/* @__PURE__ */ React.createElement(
|
|
20072
|
+
"button",
|
|
20073
|
+
{
|
|
20074
|
+
onClick: toggleWidget,
|
|
20075
|
+
className: "absolute right-3 top-1/2 transform -translate-y-1/2 hover:opacity-80 transition-opacity",
|
|
20076
|
+
style: {
|
|
20077
|
+
background: "none",
|
|
20078
|
+
border: "none",
|
|
20079
|
+
cursor: "pointer",
|
|
20080
|
+
fontSize: isMobile ? "28px" : "24px",
|
|
20081
|
+
color: getContrastColor(headerConfig.background_color || "#111827"),
|
|
20082
|
+
width: isMobile ? "40px" : "36px",
|
|
20083
|
+
height: isMobile ? "40px" : "36px",
|
|
20084
|
+
display: "flex",
|
|
20085
|
+
alignItems: "center",
|
|
20086
|
+
justifyContent: "center",
|
|
20087
|
+
padding: "0"
|
|
20088
|
+
}
|
|
20089
|
+
},
|
|
20090
|
+
"×"
|
|
20091
|
+
),
|
|
20092
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center justify-center" }, headerConfig.logo_url && /* @__PURE__ */ React.createElement(
|
|
20093
|
+
"img",
|
|
20094
|
+
{
|
|
20095
|
+
src: headerConfig.logo_url,
|
|
20096
|
+
alt: "logo",
|
|
20097
|
+
style: {
|
|
20098
|
+
width: isMobile ? "28px" : "32px",
|
|
20099
|
+
height: isMobile ? "28px" : "32px",
|
|
20100
|
+
marginBottom: isMobile ? "6px" : "8px",
|
|
20101
|
+
borderRadius: "4px"
|
|
20102
|
+
}
|
|
20103
|
+
}
|
|
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"))
|
|
20105
|
+
),
|
|
20106
|
+
/* @__PURE__ */ React.createElement(
|
|
20107
|
+
"div",
|
|
20108
|
+
{
|
|
20109
|
+
ref: chatContainerRef,
|
|
20110
|
+
className: "flex-1 relative overflow-y-auto",
|
|
20111
|
+
style: {
|
|
20112
|
+
backgroundColor: appearanceConfig.background_color || "#F9FAFB",
|
|
20113
|
+
paddingBottom: isMobile ? "8px" : "0"
|
|
20114
|
+
}
|
|
20115
|
+
},
|
|
20116
|
+
appearanceConfig.background_image && /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 pointer-events-none", style: {
|
|
20117
|
+
backgroundImage: `url(${appearanceConfig.background_image})`,
|
|
20118
|
+
backgroundSize: appearanceConfig.background_size || "cover",
|
|
20119
|
+
backgroundPosition: appearanceConfig.background_position || "center",
|
|
20120
|
+
backgroundRepeat: appearanceConfig.background_repeat || "no-repeat",
|
|
20121
|
+
opacity: appearanceConfig.background_opacity ?? 1,
|
|
20122
|
+
zIndex: 0
|
|
20123
|
+
} }),
|
|
20124
|
+
/* @__PURE__ */ React.createElement("div", { className: "relative z-10 p-3 sm:p-4 space-y-2 sm:space-y-3", style: {
|
|
20125
|
+
backgroundColor: appearanceConfig.inner_background_color !== void 0 ? appearanceConfig.inner_background_color : "transparent",
|
|
20126
|
+
paddingBottom: isMobile ? "20px" : "16px"
|
|
20127
|
+
} }, combinedMessages.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "flex justify-start" }, /* @__PURE__ */ React.createElement(
|
|
20128
|
+
"div",
|
|
20129
|
+
{
|
|
20130
|
+
className: `text-left p-3 inline-block rounded-lg`,
|
|
20131
|
+
style: {
|
|
20132
|
+
backgroundColor: chatAreaConfig.ai_response_color || "#E2E8F0",
|
|
20133
|
+
borderRadius: chatAreaConfig.chat_bubble_radius || "16px",
|
|
20134
|
+
color: chatAreaConfig.ai_font_color || "#111827",
|
|
20135
|
+
fontSize: isMobile ? "13px" : "14px",
|
|
20136
|
+
maxWidth: isMobile ? "85%" : "80%",
|
|
20137
|
+
wordBreak: "break-word"
|
|
20138
|
+
}
|
|
20139
|
+
},
|
|
20140
|
+
inputAreaConfig.first_ai_message || "Hello! How can I assist you today?"
|
|
20141
|
+
)) : combinedMessages.map((message) => /* @__PURE__ */ React.createElement("div", { key: message.id, className: `flex ${message.role === "user" ? "justify-end" : "justify-start"}` }, /* @__PURE__ */ React.createElement(
|
|
20142
|
+
"div",
|
|
20143
|
+
{
|
|
20144
|
+
className: `p-3 inline-block rounded-lg`,
|
|
20145
|
+
style: {
|
|
20146
|
+
backgroundColor: message.role === "user" ? chatAreaConfig.user_response_color || "#4ADE80" : message.isError ? "#FEE2E2" : chatAreaConfig.ai_response_color || "#E2E8F0",
|
|
20147
|
+
borderRadius: chatAreaConfig.chat_bubble_radius || "16px",
|
|
20148
|
+
fontSize: isMobile ? "13px" : "14px",
|
|
20149
|
+
color: message.role === "user" ? chatAreaConfig.user_font_color || "#111827" : message.isError ? "#991B1B" : chatAreaConfig.ai_font_color || "#111827",
|
|
20150
|
+
marginBottom: isMobile ? "4px" : "2px",
|
|
20151
|
+
maxWidth: isMobile ? "85%" : "80%",
|
|
20152
|
+
wordBreak: "break-word"
|
|
20153
|
+
}
|
|
20154
|
+
},
|
|
20155
|
+
message.role !== "system" ? /* @__PURE__ */ React.createElement(Markdown, { remarkPlugins: [remarkGfm] }, message.content) : /* @__PURE__ */ React.createElement("em", { className: "text-xs opacity-70" }, message.content),
|
|
20156
|
+
chatAreaConfig.show_timestamps && message.timestamp && /* @__PURE__ */ React.createElement("div", { className: "text-xs opacity-70 mt-1 text-right" }, message.timestamp)
|
|
20157
|
+
))), localLoading && /* @__PURE__ */ React.createElement("div", { className: "flex justify-start" }, /* @__PURE__ */ React.createElement(
|
|
20158
|
+
"div",
|
|
20159
|
+
{
|
|
20160
|
+
className: "text-left p-3 inline-block max-w-[85%]",
|
|
20161
|
+
style: {
|
|
20162
|
+
backgroundColor: chatAreaConfig.ai_response_color || "#E2E8F0",
|
|
20163
|
+
borderRadius: chatAreaConfig.chat_bubble_radius || "18px",
|
|
20164
|
+
fontSize: isMobile ? "14px" : "14px",
|
|
20165
|
+
color: chatAreaConfig.ai_font_color || "#111827"
|
|
20166
|
+
}
|
|
20167
|
+
},
|
|
20168
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React.createElement("div", { className: "w-2 h-2 bg-gray-600 rounded-full animate-bounce" }), /* @__PURE__ */ React.createElement("div", { className: "w-2 h-2 bg-gray-600 rounded-full animate-bounce", style: { animationDelay: "0.1s" } }), /* @__PURE__ */ React.createElement("div", { className: "w-2 h-2 bg-gray-600 rounded-full animate-bounce", style: { animationDelay: "0.2s" } })), /* @__PURE__ */ React.createElement("span", { className: "text-sm", style: { fontSize: isMobile ? "13px" : "14px" } }, isConnecting ? "Connecting..." : "Thinking..."))
|
|
20169
|
+
)))
|
|
20170
|
+
),
|
|
20171
|
+
/* @__PURE__ */ React.createElement(
|
|
20172
|
+
"div",
|
|
20173
|
+
{
|
|
20174
|
+
className: "flex items-center gap-2 border-t flex-shrink-0",
|
|
20175
|
+
style: {
|
|
20176
|
+
backgroundColor: inputAreaConfig.background_color || "#FFF",
|
|
20177
|
+
borderColor: inputAreaConfig.border_color || "#D1D5DB",
|
|
20178
|
+
minHeight: isMobile ? 64 : 70,
|
|
20179
|
+
position: "relative",
|
|
20180
|
+
padding: isMobile ? "10px 12px" : "12px 16px",
|
|
20181
|
+
gap: isMobile ? "8px" : "12px"
|
|
20182
|
+
}
|
|
19861
20183
|
},
|
|
19862
|
-
/* @__PURE__ */ React.createElement(
|
|
19863
|
-
|
|
20184
|
+
/* @__PURE__ */ React.createElement(
|
|
20185
|
+
"input",
|
|
20186
|
+
{
|
|
20187
|
+
ref: inputRef,
|
|
20188
|
+
value: inputMessage,
|
|
20189
|
+
onChange: (e) => setInputMessage(e.target.value),
|
|
20190
|
+
onKeyPress: handleKeyPress,
|
|
20191
|
+
disabled: localLoading,
|
|
20192
|
+
className: "flex-1 px-3 py-2 border rounded-lg",
|
|
20193
|
+
style: {
|
|
20194
|
+
borderColor: inputAreaConfig.border_color || "#D1D5DB",
|
|
20195
|
+
color: inputAreaConfig.text_color || "#111827",
|
|
20196
|
+
fontSize: isMobile ? "14px" : "14px",
|
|
20197
|
+
backgroundColor: inputAreaConfig.background_color || "#FFF",
|
|
20198
|
+
minHeight: "40px",
|
|
20199
|
+
padding: isMobile ? "8px 12px" : "10px 12px",
|
|
20200
|
+
lineHeight: "1.5"
|
|
20201
|
+
},
|
|
20202
|
+
placeholder: inputAreaConfig.placeholder_text || "Type your message..."
|
|
20203
|
+
}
|
|
20204
|
+
),
|
|
20205
|
+
/* @__PURE__ */ React.createElement(
|
|
20206
|
+
"button",
|
|
20207
|
+
{
|
|
20208
|
+
onClick: performSend,
|
|
20209
|
+
disabled: localLoading || !inputMessage.trim(),
|
|
20210
|
+
className: "rounded-full flex items-center justify-center flex-shrink-0 hover:opacity-90 transition-opacity",
|
|
20211
|
+
style: {
|
|
20212
|
+
backgroundColor: ((_b = inputAreaConfig.send_button) == null ? void 0 : _b.color) || "#2563EB",
|
|
20213
|
+
width: isMobile ? "40px" : "44px",
|
|
20214
|
+
height: isMobile ? "40px" : "44px",
|
|
20215
|
+
minWidth: isMobile ? "40px" : "44px",
|
|
20216
|
+
minHeight: isMobile ? "40px" : "44px",
|
|
20217
|
+
opacity: localLoading || !inputMessage.trim() ? 0.5 : 1,
|
|
20218
|
+
transition: "opacity 0.2s",
|
|
20219
|
+
border: "none",
|
|
20220
|
+
cursor: "pointer"
|
|
20221
|
+
}
|
|
20222
|
+
},
|
|
20223
|
+
localLoading ? /* @__PURE__ */ React.createElement(
|
|
20224
|
+
"div",
|
|
20225
|
+
{
|
|
20226
|
+
className: "rounded-full animate-spin",
|
|
20227
|
+
style: {
|
|
20228
|
+
width: isMobile ? 18 : 16,
|
|
20229
|
+
height: isMobile ? 18 : 16,
|
|
20230
|
+
border: "2px solid #FFFFFF",
|
|
20231
|
+
borderTop: "2px solid transparent"
|
|
20232
|
+
}
|
|
20233
|
+
}
|
|
20234
|
+
) : /* @__PURE__ */ React.createElement(
|
|
20235
|
+
"svg",
|
|
20236
|
+
{
|
|
20237
|
+
viewBox: "0 0 24 24",
|
|
20238
|
+
fill: "currentColor",
|
|
20239
|
+
style: {
|
|
20240
|
+
width: isMobile ? 20 : 18,
|
|
20241
|
+
height: isMobile ? 20 : 18,
|
|
20242
|
+
color: "#fff"
|
|
20243
|
+
}
|
|
20244
|
+
},
|
|
20245
|
+
/* @__PURE__ */ React.createElement("path", { d: "M2 21l21-9L2 3v7l15 2-15 2v7z" })
|
|
20246
|
+
)
|
|
20247
|
+
)
|
|
19864
20248
|
),
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
20249
|
+
inputAreaConfig.show_branding !== false && /* @__PURE__ */ React.createElement(
|
|
20250
|
+
"div",
|
|
20251
|
+
{
|
|
20252
|
+
className: "text-center text-xs",
|
|
20253
|
+
style: {
|
|
20254
|
+
backgroundColor: inputAreaConfig.background_color,
|
|
20255
|
+
padding: isMobile ? "8px 12px" : "12px 16px"
|
|
20256
|
+
}
|
|
20257
|
+
},
|
|
20258
|
+
/* @__PURE__ */ React.createElement(
|
|
20259
|
+
"a",
|
|
20260
|
+
{
|
|
20261
|
+
href: "https://www.widgetkraft.com",
|
|
20262
|
+
target: "_blank",
|
|
20263
|
+
rel: "noopener noreferrer",
|
|
20264
|
+
style: {
|
|
20265
|
+
color: getContrastColor(inputAreaConfig.background_color || "#d52929ff"),
|
|
20266
|
+
fontSize: isMobile ? "11px" : "12px",
|
|
20267
|
+
textDecoration: "none"
|
|
20268
|
+
}
|
|
20269
|
+
},
|
|
20270
|
+
"Powered by WidgetKraft"
|
|
20271
|
+
)
|
|
20272
|
+
)
|
|
20273
|
+
), /* @__PURE__ */ React.createElement("style", { jsx: true }, `
|
|
20274
|
+
/* Hide scrollbar for all browsers */
|
|
20275
|
+
.hide-scrollbar::-webkit-scrollbar { display: none; }
|
|
20276
|
+
.hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
|
20277
|
+
|
|
20278
|
+
/* Mobile-first responsive design */
|
|
20279
|
+
@media (max-width: 640px) {
|
|
20280
|
+
.responsive-notification {
|
|
20281
|
+
margin: 16px auto !important;
|
|
20282
|
+
}
|
|
20283
|
+
|
|
20284
|
+
/* Improve text readability on small screens */
|
|
20285
|
+
.responsive-message-bubble {
|
|
20286
|
+
word-wrap: break-word;
|
|
20287
|
+
overflow-wrap: break-word;
|
|
20288
|
+
}
|
|
20289
|
+
}
|
|
20290
|
+
|
|
20291
|
+
@media (max-width: 480px) {
|
|
20292
|
+
.responsive-notification {
|
|
20293
|
+
margin: 12px auto !important;
|
|
20294
|
+
min-width: 85vw;
|
|
20295
|
+
}
|
|
20296
|
+
}
|
|
20297
|
+
|
|
20298
|
+
/* Touch-friendly sizes for mobile */
|
|
20299
|
+
@media (hover: none) and (pointer: coarse) {
|
|
20300
|
+
button {
|
|
20301
|
+
min-width: 44px;
|
|
20302
|
+
min-height: 44px;
|
|
20303
|
+
}
|
|
20304
|
+
|
|
20305
|
+
input {
|
|
20306
|
+
min-height: 44px;
|
|
20307
|
+
}
|
|
20308
|
+
}
|
|
20309
|
+
|
|
20310
|
+
/* Desktop optimizations */
|
|
20311
|
+
@media (min-width: 769px) {
|
|
20312
|
+
.responsive-notification:hover {
|
|
20313
|
+
transform: translateY(-2px);
|
|
20314
|
+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
|
20315
|
+
}
|
|
20316
|
+
}
|
|
20317
|
+
|
|
20318
|
+
/* Scrollbar styling for chat container */
|
|
20319
|
+
.responsive-messages-area::-webkit-scrollbar {
|
|
20320
|
+
width: 6px;
|
|
20321
|
+
}
|
|
20322
|
+
|
|
20323
|
+
.responsive-messages-area::-webkit-scrollbar-track {
|
|
20324
|
+
background: transparent;
|
|
20325
|
+
}
|
|
20326
|
+
|
|
20327
|
+
.responsive-messages-area::-webkit-scrollbar-thumb {
|
|
20328
|
+
background: #ccc;
|
|
20329
|
+
border-radius: 3px;
|
|
20330
|
+
}
|
|
20331
|
+
|
|
20332
|
+
.responsive-messages-area::-webkit-scrollbar-thumb:hover {
|
|
20333
|
+
background: #999;
|
|
20334
|
+
}
|
|
20335
|
+
`));
|
|
19878
20336
|
};
|
|
19879
20337
|
function init({ widgetId, apiKey, mode = "inline" }) {
|
|
19880
20338
|
const container = document.getElementById("livechat-root");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getwidgets/live-chat-widget",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"main": "dist/live-chat-widget.umd.js",
|
|
5
5
|
"unpkg": "dist/live-chat-widget.umd.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"build": "vite build"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"@emoji-mart/data": "^1.2.1",
|
|
15
|
+
"@emoji-mart/react": "^1.1.1",
|
|
14
16
|
"axios": "^1.6.8",
|
|
15
17
|
"lucide-react": "^0.456.0",
|
|
16
18
|
"react": "^18.3.0",
|