@cmnd-ai/chatbot-react 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/Readme.md +239 -15
  2. package/dist/ChatProvider/index.d.ts +1 -1
  3. package/dist/ChatProvider/index.js +13 -16
  4. package/dist/ChatProvider/processStream/index.js +58 -51
  5. package/dist/CmndChatBot/index.d.ts +15 -4
  6. package/dist/CmndChatBot/index.js +4 -4
  7. package/dist/components/Chatbubble.d.ts +13 -3
  8. package/dist/components/Chatbubble.js +18 -17
  9. package/dist/components/Conversation.d.ts +7 -3
  10. package/dist/components/Conversation.js +3 -2
  11. package/dist/components/ConversationCard.d.ts +18 -0
  12. package/dist/components/ConversationCard.js +83 -0
  13. package/dist/components/ConversationsPanel/index.d.ts +33 -0
  14. package/dist/components/ConversationsPanel/index.js +333 -0
  15. package/dist/components/LoadingBubble.d.ts +14 -2
  16. package/dist/components/LoadingBubble.js +8 -7
  17. package/dist/components/ScrollToBottomButton.d.ts +10 -0
  18. package/dist/components/ScrollToBottomButton.js +57 -0
  19. package/dist/constants/endpoints.d.ts +5 -0
  20. package/dist/constants/endpoints.js +6 -0
  21. package/dist/hooks/use-fetch-data.d.ts +9 -0
  22. package/dist/hooks/use-fetch-data.js +32 -0
  23. package/dist/hooks/use-messages-scroll.d.ts +15 -0
  24. package/dist/hooks/use-messages-scroll.js +80 -0
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.js +1 -0
  27. package/dist/services/getChatBotConversationsList/index.d.ts +7 -0
  28. package/dist/services/getChatBotConversationsList/index.js +11 -0
  29. package/dist/styles/index.css +110 -0
  30. package/dist/type.d.ts +32 -0
  31. package/dist/utils/format_date/index.d.ts +2 -0
  32. package/dist/utils/format_date/index.js +32 -0
  33. package/dist/utils/getConversationLocalStorageKey/index.d.ts +5 -0
  34. package/dist/utils/getConversationLocalStorageKey/index.js +2 -0
  35. package/dist/utils/getUTCDateTime/index.d.ts +2 -0
  36. package/dist/utils/getUTCDateTime/index.js +11 -0
  37. package/dist/utils/saveConversationIdToLocalStorage/index.d.ts +6 -0
  38. package/dist/utils/saveConversationIdToLocalStorage/index.js +19 -0
  39. package/package.json +1 -1
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { MessageRole } from "../type.js";
2
+ import { MessageRole, } from "../type.js";
3
3
  import Chatbubble from "./Chatbubble.js";
4
- const LoadingBubble = ({ customStyles }) => {
4
+ const LoadingBubble = ({ customStyles, chatbotId, organizationId, theme, Components, }) => {
5
5
  const color = "white";
6
- return (_jsx(Chatbubble, { isLoadingBubble: true, customStyles: customStyles, message: _jsx("div", { className: "loading-container", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", style: {
7
- margin: "auto",
8
- display: "block",
9
- shapeRendering: "auto",
10
- }, width: "60px", height: "30px", viewBox: "0 0 100 100", preserveAspectRatio: "xMidYMid", children: [_jsxs("circle", { cx: "84", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "0.25s", calcMode: "spline", keyTimes: "0;1", values: "10;0", keySplines: "0 0.5 0.5 1", begin: "0s" }), _jsx("animate", { attributeName: "fill", repeatCount: "indefinite", dur: "1s", calcMode: "discrete", keyTimes: "0;0.25;0.5;0.75;1", values: color, begin: "0s" })] }), _jsxs("circle", { cx: "16", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "0s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "0s" })] }), _jsxs("circle", { cx: "50", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.25s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.25s" })] }), _jsxs("circle", { cx: "84", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.5s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.5s" })] }), _jsxs("circle", { cx: "16", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.75s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.75s" })] })] }) }), role: MessageRole.ASSISTANT, messages: [], postSessionMessage: () => { }, id: "__loading__", setMessages: () => { }, setChatbotConversationId: () => { }, setCanSendMessage: () => { }, setIsChatLoading: () => { }, scrollToBottom: () => { } }));
6
+ const defaultLoadingIndicator = (_jsx("div", { className: "loading-container", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", style: {
7
+ margin: "auto",
8
+ display: "block",
9
+ shapeRendering: "auto",
10
+ }, width: "60px", height: "30px", viewBox: "0 0 100 100", preserveAspectRatio: "xMidYMid", children: [_jsxs("circle", { cx: "84", cy: "50", r: "10", fill: "#000", children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "0.25s", calcMode: "spline", keyTimes: "0;1", values: "10;0", keySplines: "0 0.5 0.5 1", begin: "0s" }), _jsx("animate", { attributeName: "fill", repeatCount: "indefinite", dur: "1s", calcMode: "discrete", keyTimes: "0;0.25;0.5;0.75;1", values: color, begin: "0s" })] }), _jsxs("circle", { cx: "16", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "0s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "0s" })] }), _jsxs("circle", { cx: "50", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.25s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.25s" })] }), _jsxs("circle", { cx: "84", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.5s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.5s" })] }), _jsxs("circle", { cx: "16", cy: "50", r: "10", fill: color, children: [_jsx("animate", { attributeName: "r", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "0;0;10;10;10", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.75s" }), _jsx("animate", { attributeName: "cx", repeatCount: "indefinite", dur: "1s", calcMode: "spline", keyTimes: "0;0.25;0.5;0.75;1", values: "16;16;16;50;84", keySplines: "0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1", begin: "-0.75s" })] })] }) }));
11
+ return (_jsx(Chatbubble, { chatbotId: chatbotId, organizationId: organizationId, isLoadingBubble: true, customStyles: customStyles, theme: theme, Components: Components, message: Components?.LoadingIndicator ? (_jsx(Components.LoadingIndicator, {})) : (defaultLoadingIndicator), role: MessageRole.ASSISTANT, messages: [], postSessionMessage: () => { }, id: "__loading__", setMessages: () => { }, setChatbotConversationId: () => { }, setCanSendMessage: () => { }, setIsChatLoading: () => { } }));
11
12
  };
12
13
  export default LoadingBubble;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { CustomStyles } from "../type.js";
3
+ interface ScrollToBottomButtonProps {
4
+ onClick: () => void;
5
+ isVisible: boolean;
6
+ theme?: "light" | "dark";
7
+ customStyles?: CustomStyles;
8
+ }
9
+ declare const ScrollToBottomButton: React.FC<ScrollToBottomButtonProps>;
10
+ export default ScrollToBottomButton;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { FiChevronDown } from "react-icons/fi";
3
+ const ScrollToBottomButton = ({ onClick, isVisible, theme = "light", customStyles, }) => {
4
+ const themeColors = {
5
+ light: {
6
+ background: "#1a1d24",
7
+ hover: "#2a2f3a",
8
+ icon: "#ffffff",
9
+ shadow: "rgba(0, 0, 0, 0.2)",
10
+ },
11
+ dark: {
12
+ background: "#1a1d24",
13
+ hover: "#2a2f3a",
14
+ icon: "#ffffff",
15
+ shadow: "rgba(0, 0, 0, 0.4)",
16
+ },
17
+ };
18
+ const colors = themeColors[theme];
19
+ const baseStyle = {
20
+ position: "fixed",
21
+ bottom: "100px",
22
+ right: "20px",
23
+ width: "48px",
24
+ height: "48px",
25
+ borderRadius: "50%",
26
+ backgroundColor: colors.background,
27
+ color: colors.icon,
28
+ border: "none",
29
+ cursor: "pointer",
30
+ display: "flex",
31
+ alignItems: "center",
32
+ justifyContent: "center",
33
+ boxShadow: `0 4px 12px ${colors.shadow}`,
34
+ transition: "all 0.2s ease-in-out",
35
+ opacity: isVisible ? 1 : 0,
36
+ visibility: isVisible ? "visible" : "hidden",
37
+ transform: isVisible ? "translateY(0)" : "translateY(10px)",
38
+ zIndex: 1000,
39
+ };
40
+ const hoverStyle = {
41
+ backgroundColor: colors.hover,
42
+ transform: "translateY(-2px)",
43
+ boxShadow: `0 6px 16px ${colors.shadow}`,
44
+ };
45
+ return (_jsx("button", { onClick: onClick, style: {
46
+ ...baseStyle,
47
+ ...customStyles?.scrollButtonStyle,
48
+ }, onMouseEnter: (e) => {
49
+ Object.assign(e.currentTarget.style, hoverStyle);
50
+ }, onMouseLeave: (e) => {
51
+ Object.assign(e.currentTarget.style, {
52
+ ...baseStyle,
53
+ ...customStyles?.scrollButtonStyle,
54
+ });
55
+ }, title: "Scroll to bottom", "aria-label": "Scroll to bottom", children: _jsx(FiChevronDown, { size: 20 }) }));
56
+ };
57
+ export default ScrollToBottomButton;
@@ -1,5 +1,10 @@
1
1
  declare const chatbot: {
2
2
  patchChatbotConversationMemory: (organizationId: number, chatbotId: number, chatbotConversationId: number) => string;
3
3
  deleteChatbotConversationMemory: (organizationId: number, chatbotId: number, chatbotConversationId: number, memoryKeyToDelete: string) => string;
4
+ getChatBotConversationsList: ({ organizationId, chatbotId, conversationIds, }: {
5
+ organizationId: number;
6
+ chatbotId: number;
7
+ conversationIds: number[];
8
+ }) => string;
4
9
  };
5
10
  export { chatbot };
@@ -1,5 +1,11 @@
1
1
  const chatbot = {
2
2
  patchChatbotConversationMemory: (organizationId, chatbotId, chatbotConversationId) => `/organizations/${organizationId}/chatbots/${chatbotId}/conversations/${chatbotConversationId}/memory`,
3
3
  deleteChatbotConversationMemory: (organizationId, chatbotId, chatbotConversationId, memoryKeyToDelete) => `/organizations/${organizationId}/chatbots/${chatbotId}/conversations/${chatbotConversationId}/memory/${memoryKeyToDelete}`,
4
+ getChatBotConversationsList: ({ organizationId, chatbotId, conversationIds, }) => {
5
+ const ids = Array.isArray(conversationIds)
6
+ ? conversationIds.join(",")
7
+ : conversationIds;
8
+ return `/organizations/${organizationId}/chatbots/${chatbotId}/conversations-list/${ids}`;
9
+ },
4
10
  };
5
11
  export { chatbot };
@@ -0,0 +1,9 @@
1
+ import { AxiosPromise } from "axios";
2
+ type UseFetchDataResult<T = any> = {
3
+ loading: boolean;
4
+ error: boolean;
5
+ data: T;
6
+ refetch: () => void;
7
+ };
8
+ declare const useFetchData: <T = any>(apiCallFn: () => AxiosPromise<T>, deps?: any[]) => UseFetchDataResult<T>;
9
+ export default useFetchData;
@@ -0,0 +1,32 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable react-hooks/exhaustive-deps */
3
+ import { useEffect, useState } from "react";
4
+ const useFetchData = (apiCallFn, deps = []) => {
5
+ const [loading, setLoading] = useState(true);
6
+ const [error, setError] = useState(false);
7
+ const [data, setData] = useState({});
8
+ const fetchData = async () => {
9
+ setLoading(true);
10
+ setError(false);
11
+ try {
12
+ const response = await apiCallFn();
13
+ setData(response.data);
14
+ }
15
+ catch {
16
+ setError(true);
17
+ }
18
+ finally {
19
+ setLoading(false);
20
+ }
21
+ };
22
+ useEffect(() => {
23
+ fetchData();
24
+ }, deps);
25
+ return {
26
+ loading,
27
+ error,
28
+ data,
29
+ refetch: fetchData,
30
+ };
31
+ };
32
+ export default useFetchData;
@@ -0,0 +1,15 @@
1
+ /// <reference types="react" />
2
+ import { Message } from "../type.js";
3
+ /**
4
+ * A custom hook to manage auto-scrolling of a message container.
5
+ * Automatically scrolls to the bottom when messages change, unless the user has scrolled up.
6
+ *
7
+ * @param messages - The list of messages.
8
+ * @returns An object containing the `messagesRef` for the chat timeline container and a `resetMessageScroll` function.
9
+ */
10
+ declare const useMessagesScroll: (messages: Message[]) => {
11
+ messagesRef: import("react").MutableRefObject<HTMLDivElement | null>;
12
+ resetMessagesScroll: () => void;
13
+ isMessagesScrolledToBottom: boolean;
14
+ };
15
+ export default useMessagesScroll;
@@ -0,0 +1,80 @@
1
+ import { useRef, useEffect, useState, useCallback } from "react";
2
+ /**
3
+ * A custom hook to manage auto-scrolling of a message container.
4
+ * Automatically scrolls to the bottom when messages change, unless the user has scrolled up.
5
+ *
6
+ * @param messages - The list of messages.
7
+ * @returns An object containing the `messagesRef` for the chat timeline container and a `resetMessageScroll` function.
8
+ */
9
+ const useMessagesScroll = (messages) => {
10
+ const messagesRef = useRef(null);
11
+ const [userScrolled, setUserScrolled] = useState(false);
12
+ const [isMessagesScrolledToBottom, setIsMessagesScrolledToBottom] = useState(true);
13
+ const [refAvailable, setRefAvailable] = useState(false);
14
+ // Reset userScrolled when new messages arrive (so auto-scroll resumes)
15
+ useEffect(() => {
16
+ if (messages.length > 0 && userScrolled) {
17
+ setUserScrolled(false);
18
+ }
19
+ }, [messages.length]);
20
+ // Check if ref is available
21
+ useEffect(() => {
22
+ if (messagesRef.current && !refAvailable) {
23
+ setRefAvailable(true);
24
+ }
25
+ else if (!messagesRef.current && refAvailable) {
26
+ setRefAvailable(false);
27
+ }
28
+ });
29
+ const handleMessagesScroll = useCallback(() => {
30
+ const messagesElement = messagesRef.current;
31
+ if (!messagesElement)
32
+ return;
33
+ const { scrollTop, clientHeight, scrollHeight } = messagesElement;
34
+ const isScrolledToBottom = Math.abs(scrollTop + clientHeight - scrollHeight) <= 100;
35
+ setIsMessagesScrolledToBottom(isScrolledToBottom);
36
+ }, []);
37
+ // Set up event listeners when ref becomes available
38
+ useEffect(() => {
39
+ const ref = messagesRef.current;
40
+ if (!ref || !refAvailable) {
41
+ return;
42
+ }
43
+ const onScrollUp = (e) => {
44
+ if (e.deltaY < 0 && !userScrolled) {
45
+ setUserScrolled(true);
46
+ if (ref) {
47
+ ref.removeEventListener("wheel", onScrollUp);
48
+ }
49
+ }
50
+ };
51
+ // Initial scroll position check
52
+ handleMessagesScroll();
53
+ ref.addEventListener("scroll", handleMessagesScroll);
54
+ if (!userScrolled) {
55
+ ref.addEventListener("wheel", onScrollUp);
56
+ }
57
+ return () => {
58
+ ref.removeEventListener("wheel", onScrollUp);
59
+ ref.removeEventListener("scroll", handleMessagesScroll);
60
+ };
61
+ }, [refAvailable, userScrolled, handleMessagesScroll]); // Run when ref becomes available or userScrolled changes
62
+ useEffect(() => {
63
+ if (messagesRef.current && !userScrolled) {
64
+ // Use setTimeout to ensure DOM has updated
65
+ setTimeout(() => {
66
+ if (messagesRef.current) {
67
+ messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
68
+ }
69
+ }, 0);
70
+ }
71
+ }, [messages, userScrolled]);
72
+ const resetMessagesScroll = () => {
73
+ if (messagesRef.current) {
74
+ messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
75
+ }
76
+ setUserScrolled(false);
77
+ };
78
+ return { messagesRef, resetMessagesScroll, isMessagesScrolledToBottom };
79
+ };
80
+ export default useMessagesScroll;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as ChatProvider } from "./ChatProvider/index.js";
2
- export { CmndChatContext, InputFieldProps, SendButtonProps, CustomStyles, CMNDChatMemory, } from "./type.js";
2
+ export { ConversationsPanel } from "./components/ConversationsPanel/index.js";
3
+ export { CmndChatContext, InputFieldProps, SendButtonProps, CustomStyles, CMNDChatMemory, ChatbotConversation, ChatbotConversationsResponse, } from "./type.js";
3
4
  export { setCurrentConversationMemory, deleteCurrentConversationMemory, } from "./ChatProvider/index.js";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default as ChatProvider } from "./ChatProvider/index.js";
2
+ export { ConversationsPanel } from "./components/ConversationsPanel/index.js";
2
3
  export { setCurrentConversationMemory, deleteCurrentConversationMemory, } from "./ChatProvider/index.js";
@@ -0,0 +1,7 @@
1
+ declare const getChatBotConversationsList: ({ organizationId, chatbotId, conversationIds, baseUrl, }: {
2
+ organizationId: number;
3
+ chatbotId: number;
4
+ conversationIds: number[];
5
+ baseUrl: string;
6
+ }) => Promise<import("axios").AxiosResponse<any, any>>;
7
+ export default getChatBotConversationsList;
@@ -0,0 +1,11 @@
1
+ import axios from "axios";
2
+ import { chatbot } from "../../constants/endpoints.js";
3
+ const getChatBotConversationsList = ({ organizationId, chatbotId, conversationIds, baseUrl, }) => {
4
+ const endpoint = chatbot.getChatBotConversationsList({
5
+ organizationId,
6
+ chatbotId,
7
+ conversationIds,
8
+ });
9
+ return axios.get(`${baseUrl}${endpoint}`);
10
+ };
11
+ export default getChatBotConversationsList;
@@ -12,6 +12,7 @@
12
12
  justify-content: center;
13
13
  align-items: center;
14
14
  }
15
+
15
16
  .cmnd-conversations-messages {
16
17
  display: flex;
17
18
  flex-direction: column;
@@ -202,3 +203,112 @@
202
203
  font-size: 14px;
203
204
  }
204
205
  }
206
+
207
+ /* Conversation Panel Styles */
208
+ .cmnd-conversations-panel {
209
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
210
+ "Ubuntu", "Cantarell", sans-serif;
211
+ }
212
+
213
+ .cmnd-conversations-sidebar {
214
+ scrollbar-width: thin;
215
+ scrollbar-color: #3d4354 #20232c;
216
+ }
217
+
218
+ .cmnd-conversations-sidebar::-webkit-scrollbar {
219
+ width: 6px;
220
+ }
221
+
222
+ .cmnd-conversations-sidebar::-webkit-scrollbar-track {
223
+ background: #20232c;
224
+ }
225
+
226
+ .cmnd-conversations-sidebar::-webkit-scrollbar-thumb {
227
+ background: #3d4354;
228
+ border-radius: 3px;
229
+ }
230
+
231
+ .cmnd-conversations-sidebar::-webkit-scrollbar-thumb:hover {
232
+ background: #4a5568;
233
+ }
234
+
235
+ .cmnd-conversation-card {
236
+ transition: all 0.2s ease;
237
+ }
238
+
239
+ .cmnd-conversation-card:hover {
240
+ background-color: #2d3748 !important;
241
+ border-color: #4a5568 !important;
242
+ }
243
+
244
+ .cmnd-conversation-card-active {
245
+ background-color: #3d4354 !important;
246
+ border-color: #5a67d8 !important;
247
+ }
248
+
249
+ .cmnd-conversation-delete {
250
+ opacity: 0;
251
+ transition: opacity 0.2s ease;
252
+ }
253
+
254
+ .cmnd-conversation-card:hover .cmnd-conversation-delete {
255
+ opacity: 1;
256
+ }
257
+
258
+ /* Responsive design for mobile */
259
+ @media screen and (max-width: 768px) {
260
+ .cmnd-conversations-sidebar {
261
+ width: 100% !important;
262
+ border-right: none !important;
263
+ border-bottom: 1px solid #3d4354;
264
+ max-height: 200px;
265
+ }
266
+
267
+ .cmnd-conversations-panel {
268
+ flex-direction: column;
269
+ }
270
+
271
+ .cmnd-conversation-card {
272
+ padding: 8px 12px !important;
273
+ }
274
+
275
+ .cmnd-conversation-title {
276
+ font-size: 13px !important;
277
+ }
278
+
279
+ .cmnd-conversation-date {
280
+ font-size: 11px !important;
281
+ }
282
+ }
283
+
284
+ /* Scroll to bottom button */
285
+ .cmnd-scroll-to-bottom-button {
286
+ position: fixed;
287
+ bottom: 100px;
288
+ right: 20px;
289
+ width: 48px;
290
+ height: 48px;
291
+ border-radius: 50%;
292
+ border: none;
293
+ cursor: pointer;
294
+ display: flex;
295
+ align-items: center;
296
+ justify-content: center;
297
+ transition: all 0.2s ease-in-out;
298
+ z-index: 1000;
299
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
300
+ }
301
+
302
+ .cmnd-scroll-to-bottom-button:hover {
303
+ transform: translateY(-2px);
304
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
305
+ }
306
+
307
+ @media screen and (max-width: 768px) {
308
+ .cmnd-scroll-to-bottom-button {
309
+ bottom: 80px;
310
+ right: 15px;
311
+ width: 44px;
312
+ height: 44px;
313
+ }
314
+ }
package/dist/type.d.ts CHANGED
@@ -126,8 +126,40 @@ export interface CustomStyles {
126
126
  chatbubbleStyle?: CSSProperties;
127
127
  botChatbubbleStyle?: CSSProperties;
128
128
  userChatbubbleStyle?: CSSProperties;
129
+ scrollButtonStyle?: CSSProperties;
130
+ panelStyle?: CSSProperties;
131
+ sidebarStyle?: CSSProperties;
132
+ conversationListStyle?: CSSProperties;
133
+ headerStyle?: CSSProperties;
134
+ newChatButtonStyle?: CSSProperties;
135
+ refreshButtonStyle?: CSSProperties;
136
+ conversationCardStyle?: CSSProperties;
137
+ activeConversationCardStyle?: CSSProperties;
138
+ titleStyle?: CSSProperties;
139
+ dateStyle?: CSSProperties;
140
+ deleteButtonStyle?: CSSProperties;
129
141
  }
130
142
  export interface CMNDChatMemory {
131
143
  [key: string]: any;
132
144
  }
145
+ export interface ChatbotConversationMessage {
146
+ id?: string;
147
+ role: "user" | "assistant";
148
+ message: string;
149
+ unuseful: boolean;
150
+ hiddenFromUser: boolean;
151
+ }
152
+ export interface ChatbotConversation {
153
+ chatbotConversationId: number;
154
+ messages: ChatbotConversationMessage[];
155
+ chatbotId: number;
156
+ chatbotConversationTitle: string;
157
+ createdAt: string;
158
+ updatedAt: string;
159
+ totalCostSoFar: number;
160
+ totalTokensSoFar: number;
161
+ }
162
+ export interface ChatbotConversationsResponse {
163
+ chatbotConversations: ChatbotConversation[];
164
+ }
133
165
  export {};
@@ -0,0 +1,2 @@
1
+ declare const formatDate: (date: string) => string;
2
+ export default formatDate;
@@ -0,0 +1,32 @@
1
+ const formatDate = (date) => {
2
+ // Parse the input date string as UTC
3
+ const timestamp = new Date(date + " UTC");
4
+ const now = new Date();
5
+ const localTimestampYear = timestamp.getFullYear();
6
+ const localTimestampMonth = timestamp.getMonth();
7
+ const localTimestampDate = timestamp.getDate();
8
+ const nowYear = now.getFullYear();
9
+ const nowMonth = now.getMonth();
10
+ const nowDate = now.getDate();
11
+ const hours = timestamp.getHours();
12
+ const minutes = timestamp.getMinutes();
13
+ if (nowYear === localTimestampYear &&
14
+ nowMonth === localTimestampMonth &&
15
+ nowDate === localTimestampDate) {
16
+ return `Today at ${hours}:${minutes < 10 ? "0" + minutes : minutes}`;
17
+ }
18
+ else if (nowYear === localTimestampYear &&
19
+ nowMonth === localTimestampMonth &&
20
+ nowDate - localTimestampDate === 1) {
21
+ return `Yesterday at ${hours}:${minutes < 10 ? "0" + minutes : minutes}`;
22
+ }
23
+ else {
24
+ // Use toLocaleDateString to format in local time zone
25
+ return timestamp.toLocaleDateString("en-GB", {
26
+ month: "long",
27
+ day: "numeric",
28
+ year: "numeric",
29
+ });
30
+ }
31
+ };
32
+ export default formatDate;
@@ -0,0 +1,5 @@
1
+ declare const getConversationLocalStorageKey: ({ organizationId, chatbotId, }: {
2
+ organizationId: number;
3
+ chatbotId: number;
4
+ }) => string;
5
+ export default getConversationLocalStorageKey;
@@ -0,0 +1,2 @@
1
+ const getConversationLocalStorageKey = ({ organizationId, chatbotId, }) => `cmnd::orgId::${organizationId}::chatBotId::${chatbotId}`;
2
+ export default getConversationLocalStorageKey;
@@ -0,0 +1,2 @@
1
+ declare const getUTCDateTime: () => string;
2
+ export default getUTCDateTime;
@@ -0,0 +1,11 @@
1
+ const getUTCDateTime = () => {
2
+ const now = new Date();
3
+ const year = now.getUTCFullYear();
4
+ const month = String(now.getUTCMonth() + 1).padStart(2, "0");
5
+ const day = String(now.getUTCDate()).padStart(2, "0");
6
+ const hours = String(now.getUTCHours()).padStart(2, "0");
7
+ const minutes = String(now.getUTCMinutes()).padStart(2, "0");
8
+ const seconds = String(now.getUTCSeconds()).padStart(2, "0");
9
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
10
+ };
11
+ export default getUTCDateTime;
@@ -0,0 +1,6 @@
1
+ declare const saveConversationIdToLocalStorage: ({ chatbotConversationId, chatbotId, organizationId, }: {
2
+ chatbotConversationId: number;
3
+ chatbotId: number;
4
+ organizationId: number;
5
+ }) => Promise<void>;
6
+ export default saveConversationIdToLocalStorage;
@@ -0,0 +1,19 @@
1
+ import getConversationLocalStorageKey from "../getConversationLocalStorageKey/index.js";
2
+ const saveConversationIdToLocalStorage = async ({ chatbotConversationId, chatbotId, organizationId, }) => {
3
+ try {
4
+ const key = getConversationLocalStorageKey({
5
+ organizationId,
6
+ chatbotId: Number(chatbotId),
7
+ });
8
+ //get the conversation ids from local storage
9
+ const conversations = JSON.parse(localStorage.getItem(key) || "[]") ?? [];
10
+ if (!conversations.includes(chatbotConversationId)) {
11
+ conversations.push(chatbotConversationId);
12
+ localStorage.setItem(key, JSON.stringify(conversations));
13
+ }
14
+ }
15
+ catch (error) {
16
+ console.error("error saving conversation id to local storage", error);
17
+ }
18
+ };
19
+ export default saveConversationIdToLocalStorage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmnd-ai/chatbot-react",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "main": "dist/index.js",
5
5
  "description": "",
6
6
  "type": "module",