@cmnd-ai/chatbot-react 1.7.1 → 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.
- package/Readme.md +239 -15
- package/dist/ChatProvider/index.d.ts +1 -1
- package/dist/ChatProvider/index.js +13 -16
- package/dist/CmndChatBot/index.d.ts +15 -4
- package/dist/CmndChatBot/index.js +4 -4
- package/dist/components/Chatbubble.d.ts +13 -3
- package/dist/components/Chatbubble.js +18 -17
- package/dist/components/Conversation.d.ts +7 -3
- package/dist/components/Conversation.js +3 -2
- package/dist/components/ConversationCard.d.ts +18 -0
- package/dist/components/ConversationCard.js +83 -0
- package/dist/components/ConversationsPanel/index.d.ts +33 -0
- package/dist/components/ConversationsPanel/index.js +333 -0
- package/dist/components/LoadingBubble.d.ts +14 -2
- package/dist/components/LoadingBubble.js +8 -7
- package/dist/components/ScrollToBottomButton.d.ts +10 -0
- package/dist/components/ScrollToBottomButton.js +57 -0
- package/dist/constants/endpoints.d.ts +5 -0
- package/dist/constants/endpoints.js +6 -0
- package/dist/hooks/use-fetch-data.d.ts +9 -0
- package/dist/hooks/use-fetch-data.js +32 -0
- package/dist/hooks/use-messages-scroll.d.ts +15 -0
- package/dist/hooks/use-messages-scroll.js +80 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/services/getChatBotConversationsList/index.d.ts +7 -0
- package/dist/services/getChatBotConversationsList/index.js +11 -0
- package/dist/styles/index.css +110 -0
- package/dist/type.d.ts +32 -0
- package/dist/utils/format_date/index.d.ts +2 -0
- package/dist/utils/format_date/index.js +32 -0
- package/dist/utils/getConversationLocalStorageKey/index.d.ts +5 -0
- package/dist/utils/getConversationLocalStorageKey/index.js +2 -0
- package/dist/utils/getUTCDateTime/index.d.ts +2 -0
- package/dist/utils/getUTCDateTime/index.js +11 -0
- package/dist/utils/saveConversationIdToLocalStorage/index.d.ts +6 -0
- package/dist/utils/saveConversationIdToLocalStorage/index.js +19 -0
- package/package.json +1 -1
|
@@ -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 {
|
|
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
|
@@ -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;
|
package/dist/styles/index.css
CHANGED
|
@@ -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,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,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,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;
|