@copilotkit/react-ui 0.22.0 → 0.36.0-mme-push-to-talk.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/.turbo/turbo-build.log +204 -178
- package/CHANGELOG.md +12 -0
- package/dist/chunk-5ASYNEHX.mjs +53 -0
- package/dist/chunk-5ASYNEHX.mjs.map +1 -0
- package/dist/{chunk-MBYUBR3F.mjs → chunk-A7J4KGLP.mjs} +2 -2
- package/dist/chunk-DMAQBCTX.mjs +95 -0
- package/dist/chunk-DMAQBCTX.mjs.map +1 -0
- package/dist/{chunk-KE3N45ZY.mjs → chunk-DRNCOXZO.mjs} +3 -4
- package/dist/chunk-DRNCOXZO.mjs.map +1 -0
- package/dist/{chunk-7YXG7D47.mjs → chunk-FWTPMPSN.mjs} +37 -2
- package/dist/chunk-FWTPMPSN.mjs.map +1 -0
- package/dist/chunk-JPX5ODUX.mjs +266 -0
- package/dist/chunk-JPX5ODUX.mjs.map +1 -0
- package/dist/chunk-KZME7C5S.mjs +76 -0
- package/dist/chunk-KZME7C5S.mjs.map +1 -0
- package/dist/{chunk-7JYUCW7H.mjs → chunk-MWFHYCQB.mjs} +2 -2
- package/dist/chunk-PEDSZYHE.mjs +36 -0
- package/dist/chunk-PEDSZYHE.mjs.map +1 -0
- package/dist/{chunk-YEHO5VMA.mjs → chunk-PGZDQT74.mjs} +2 -2
- package/dist/{chunk-MRXNTQOX.mjs → chunk-SKC7AJIV.mjs} +3 -1
- package/dist/chunk-T26KLXLH.mjs +1 -0
- package/dist/{chunk-T3WYKWNC.mjs → chunk-UVMROYDT.mjs} +5 -5
- package/dist/chunk-UVMROYDT.mjs.map +1 -0
- package/dist/{chunk-YAORLSQ3.mjs → chunk-XWWMYJJF.mjs} +5 -5
- package/dist/chunk-XWWMYJJF.mjs.map +1 -0
- package/dist/{chunk-73EBDGYK.mjs → chunk-XYM43AHP.mjs} +5 -5
- package/dist/chunk-XYM43AHP.mjs.map +1 -0
- package/dist/chunk-Z45ZEXJW.mjs +32 -0
- package/dist/chunk-Z45ZEXJW.mjs.map +1 -0
- package/dist/{chunk-WDHLWSSU.mjs → chunk-ZKLK3M77.mjs} +3 -3
- package/dist/components/chat/Button.d.ts +1 -1
- package/dist/components/chat/Button.js +30 -2
- package/dist/components/chat/Button.js.map +1 -1
- package/dist/components/chat/Button.mjs +4 -4
- package/dist/components/chat/Chat.d.ts +3 -2
- package/dist/components/chat/Chat.js +397 -86
- package/dist/components/chat/Chat.js.map +1 -1
- package/dist/components/chat/Chat.mjs +13 -11
- package/dist/components/chat/ChatContext.d.ts +6 -1
- package/dist/components/chat/ChatContext.js +29 -28
- package/dist/components/chat/ChatContext.js.map +1 -1
- package/dist/components/chat/ChatContext.mjs +3 -3
- package/dist/components/chat/CodeBlock.js.map +1 -1
- package/dist/components/chat/CodeBlock.mjs +3 -3
- package/dist/components/chat/Header.js.map +1 -1
- package/dist/components/chat/Header.mjs +4 -4
- package/dist/components/chat/Icons.d.ts +2 -1
- package/dist/components/chat/Icons.js +36 -0
- package/dist/components/chat/Icons.js.map +1 -1
- package/dist/components/chat/Icons.mjs +4 -2
- package/dist/components/chat/Input.d.ts +1 -1
- package/dist/components/chat/Input.js +1 -2
- package/dist/components/chat/Input.js.map +1 -1
- package/dist/components/chat/Input.mjs +4 -4
- package/dist/components/chat/Markdown.js.map +1 -1
- package/dist/components/chat/Markdown.mjs +4 -4
- package/dist/components/chat/Messages.d.ts +1 -1
- package/dist/components/chat/Messages.js +2 -2
- package/dist/components/chat/Messages.js.map +1 -1
- package/dist/components/chat/Messages.mjs +6 -6
- package/dist/components/chat/Popup.d.ts +52 -1
- package/dist/components/chat/Popup.js +401 -90
- package/dist/components/chat/Popup.js.map +1 -1
- package/dist/components/chat/Popup.mjs +14 -12
- package/dist/components/chat/Response.js.map +1 -1
- package/dist/components/chat/Response.mjs +4 -4
- package/dist/components/chat/Sidebar.d.ts +3 -5
- package/dist/components/chat/Sidebar.js +403 -92
- package/dist/components/chat/Sidebar.js.map +1 -1
- package/dist/components/chat/Sidebar.mjs +14 -12
- package/dist/components/chat/Suggestion.d.ts +14 -0
- package/dist/components/chat/Suggestion.js +172 -0
- package/dist/components/chat/Suggestion.js.map +1 -0
- package/dist/components/chat/Suggestion.mjs +11 -0
- package/dist/components/chat/Suggestion.mjs.map +1 -0
- package/dist/components/chat/Textarea.mjs +1 -1
- package/dist/components/chat/Window.mjs +1 -1
- package/dist/components/chat/audio.d.ts +7 -0
- package/dist/components/chat/audio.js +77 -0
- package/dist/components/chat/audio.js.map +1 -0
- package/dist/components/chat/audio.mjs +10 -0
- package/dist/components/chat/audio.mjs.map +1 -0
- package/dist/components/chat/index.d.ts +2 -1
- package/dist/components/chat/index.js +407 -96
- package/dist/components/chat/index.js.map +1 -1
- package/dist/components/chat/index.mjs +15 -13
- package/dist/components/chat/props.d.ts +11 -2
- package/dist/components/chat/props.js.map +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +407 -96
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +15 -13
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +61 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +28 -1
- package/dist/hooks/use-copilot-chat-suggestions.d.ts +65 -0
- package/dist/hooks/use-copilot-chat-suggestions.js +78 -0
- package/dist/hooks/use-copilot-chat-suggestions.js.map +1 -0
- package/dist/hooks/use-copilot-chat-suggestions.mjs +28 -0
- package/dist/hooks/use-copilot-chat-suggestions.mjs.map +1 -0
- package/dist/hooks/use-copy-to-clipboard.mjs +1 -1
- package/dist/index.css +41 -5
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +435 -98
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +23 -17
- package/dist/lib/utils.mjs +1 -1
- package/dist/types/suggestions.d.ts +28 -0
- package/dist/types/suggestions.js +19 -0
- package/dist/types/suggestions.js.map +1 -0
- package/dist/types/suggestions.mjs +1 -0
- package/dist/types/suggestions.mjs.map +1 -0
- package/package.json +6 -6
- package/src/components/chat/Button.tsx +36 -4
- package/src/components/chat/Chat.tsx +204 -16
- package/src/components/chat/ChatContext.tsx +44 -32
- package/src/components/chat/Icons.tsx +27 -0
- package/src/components/chat/Input.tsx +1 -2
- package/src/components/chat/Messages.tsx +3 -3
- package/src/components/chat/Popup.tsx +52 -6
- package/src/components/chat/Sidebar.tsx +59 -6
- package/src/components/chat/Suggestion.tsx +105 -0
- package/src/components/chat/audio.ts +26 -0
- package/src/components/chat/props.ts +11 -1
- package/src/css/messages.css +7 -1
- package/src/css/response.css +1 -4
- package/src/css/suggestions.css +35 -0
- package/src/hooks/index.ts +1 -1
- package/src/hooks/use-copilot-chat-suggestions.tsx +91 -0
- package/src/styles.css +1 -0
- package/src/types/suggestions.ts +30 -0
- package/typedoc.json +4 -0
- package/dist/chunk-6U3O2JZP.mjs +0 -100
- package/dist/chunk-6U3O2JZP.mjs.map +0 -1
- package/dist/chunk-73EBDGYK.mjs.map +0 -1
- package/dist/chunk-7LMXXGJT.mjs +0 -75
- package/dist/chunk-7LMXXGJT.mjs.map +0 -1
- package/dist/chunk-7YXG7D47.mjs.map +0 -1
- package/dist/chunk-H4VKQGVU.mjs +0 -1
- package/dist/chunk-KE3N45ZY.mjs.map +0 -1
- package/dist/chunk-T3WYKWNC.mjs.map +0 -1
- package/dist/chunk-YAORLSQ3.mjs.map +0 -1
- package/dist/chunk-YTIGBBTC.mjs +0 -25
- package/dist/chunk-YTIGBBTC.mjs.map +0 -1
- /package/dist/{chunk-MBYUBR3F.mjs.map → chunk-A7J4KGLP.mjs.map} +0 -0
- /package/dist/{chunk-7JYUCW7H.mjs.map → chunk-MWFHYCQB.mjs.map} +0 -0
- /package/dist/{chunk-YEHO5VMA.mjs.map → chunk-PGZDQT74.mjs.map} +0 -0
- /package/dist/{chunk-H4VKQGVU.mjs.map → chunk-SKC7AJIV.mjs.map} +0 -0
- /package/dist/{chunk-MRXNTQOX.mjs.map → chunk-T26KLXLH.mjs.map} +0 -0
- /package/dist/{chunk-WDHLWSSU.mjs.map → chunk-ZKLK3M77.mjs.map} +0 -0
|
@@ -1,12 +1,44 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
1
2
|
import { ButtonProps } from "./props";
|
|
2
3
|
import { useChatContext } from "./ChatContext";
|
|
3
4
|
|
|
4
|
-
export const Button = ({ open, setOpen }: ButtonProps) => {
|
|
5
|
+
export const Button = ({ open, setOpen, pushToTalk, setPushToTalk }: ButtonProps) => {
|
|
5
6
|
const context = useChatContext();
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
|
8
|
+
const [isLongPress, setIsLongPress] = useState(false);
|
|
9
|
+
|
|
10
|
+
const handleMouseDown = () => {
|
|
11
|
+
timerRef.current = setTimeout(() => {
|
|
12
|
+
setPushToTalk(true);
|
|
13
|
+
setIsLongPress(true);
|
|
14
|
+
}, 500); // 500ms for long press
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const handleMouseUp = () => {
|
|
18
|
+
if (timerRef.current) {
|
|
19
|
+
clearTimeout(timerRef.current);
|
|
20
|
+
setPushToTalk(false);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const handleClick = () => {
|
|
25
|
+
if (!isLongPress) {
|
|
26
|
+
setOpen(!open);
|
|
27
|
+
} else {
|
|
28
|
+
setIsLongPress(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// we want to handle the mouse up event event outside of the button component
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
35
|
+
return () => {
|
|
36
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
8
40
|
return (
|
|
9
|
-
<div onClick={
|
|
41
|
+
<div onClick={handleClick} onMouseDown={handleMouseDown}>
|
|
10
42
|
<button
|
|
11
43
|
className={`copilotKitButton ${open ? "open" : ""}`}
|
|
12
44
|
aria-label={open ? "Close Chat" : "Open Chat"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import React, { useEffect } from "react";
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { CopilotChatIcons, ChatContextProvider, CopilotChatLabels } from "./ChatContext";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
SystemMessageFunction,
|
|
5
|
+
extract,
|
|
6
|
+
useCopilotChat,
|
|
7
|
+
useCopilotContext,
|
|
8
|
+
} from "@copilotkit/react-core";
|
|
4
9
|
import {
|
|
5
10
|
ButtonProps,
|
|
6
11
|
HeaderProps,
|
|
@@ -8,6 +13,7 @@ import {
|
|
|
8
13
|
MessagesProps,
|
|
9
14
|
InputProps,
|
|
10
15
|
ResponseButtonProps,
|
|
16
|
+
SuggestionsProps,
|
|
11
17
|
} from "./props";
|
|
12
18
|
import { Window as DefaultWindow } from "./Window";
|
|
13
19
|
import { Button as DefaultButton } from "./Button";
|
|
@@ -16,6 +22,10 @@ import { Messages as DefaultMessages } from "./Messages";
|
|
|
16
22
|
import { Input as DefaultInput } from "./Input";
|
|
17
23
|
import { nanoid } from "nanoid";
|
|
18
24
|
import { ResponseButton as DefaultResponseButton } from "./Response";
|
|
25
|
+
import { Suggestion, reloadSuggestions } from "./Suggestion";
|
|
26
|
+
import { CopilotChatSuggestion, CopilotChatSuggestionConfiguration } from "../../types/suggestions";
|
|
27
|
+
import { requestMicAndPlaybackPermission } from "./audio";
|
|
28
|
+
import { Message } from "@copilotkit/shared";
|
|
19
29
|
|
|
20
30
|
/**
|
|
21
31
|
* Props for CopilotChat component.
|
|
@@ -66,8 +76,8 @@ export interface CopilotChatProps {
|
|
|
66
76
|
|
|
67
77
|
/**
|
|
68
78
|
* The shortcut key to open the chat window.
|
|
69
|
-
* Uses Command
|
|
70
|
-
* @default "
|
|
79
|
+
* Uses Command-[shortcut] on a Mac and Ctrl-[shortcut] on Windows.
|
|
80
|
+
* @default "/"
|
|
71
81
|
*/
|
|
72
82
|
shortcut?: string;
|
|
73
83
|
|
|
@@ -136,6 +146,8 @@ export interface CopilotChatProps {
|
|
|
136
146
|
children?: React.ReactNode;
|
|
137
147
|
}
|
|
138
148
|
|
|
149
|
+
const SUGGESTIONS_DEBOUNCE_TIMEOUT = 1000;
|
|
150
|
+
|
|
139
151
|
export const CopilotChat = ({
|
|
140
152
|
instructions,
|
|
141
153
|
defaultOpen = false,
|
|
@@ -164,31 +176,190 @@ export const CopilotChat = ({
|
|
|
164
176
|
additionalInstructions: instructions,
|
|
165
177
|
});
|
|
166
178
|
|
|
179
|
+
const [currentSuggestions, setCurrentSuggestions] = React.useState<CopilotChatSuggestion[]>([]);
|
|
180
|
+
const suggestionsAbortControllerRef = useRef<AbortController | null>(null);
|
|
181
|
+
const debounceTimerRef = useRef<any>();
|
|
182
|
+
|
|
183
|
+
const abortSuggestions = () => {
|
|
184
|
+
suggestionsAbortControllerRef.current?.abort();
|
|
185
|
+
suggestionsAbortControllerRef.current = null;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const context = useCopilotContext();
|
|
189
|
+
|
|
190
|
+
const [chatSuggestionConfiguration, setChatSuggestionConfiguration] = useState<{
|
|
191
|
+
[key: string]: CopilotChatSuggestionConfiguration;
|
|
192
|
+
}>({});
|
|
193
|
+
|
|
194
|
+
const addChatSuggestionConfiguration = (
|
|
195
|
+
id: string,
|
|
196
|
+
suggestion: CopilotChatSuggestionConfiguration,
|
|
197
|
+
) => {
|
|
198
|
+
setChatSuggestionConfiguration((prev) => ({ ...prev, [id]: suggestion }));
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const removeChatSuggestion = (id: string) => {
|
|
202
|
+
setChatSuggestionConfiguration((prev) => {
|
|
203
|
+
const { [id]: _, ...rest } = prev;
|
|
204
|
+
return rest;
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
167
208
|
useEffect(() => {
|
|
168
209
|
onInProgress?.(isLoading);
|
|
169
|
-
}, [isLoading]);
|
|
170
210
|
|
|
171
|
-
|
|
211
|
+
abortSuggestions();
|
|
212
|
+
|
|
213
|
+
debounceTimerRef.current = setTimeout(
|
|
214
|
+
() => {
|
|
215
|
+
if (!isLoading && Object.keys(chatSuggestionConfiguration).length !== 0) {
|
|
216
|
+
suggestionsAbortControllerRef.current = new AbortController();
|
|
217
|
+
reloadSuggestions(
|
|
218
|
+
context,
|
|
219
|
+
chatSuggestionConfiguration,
|
|
220
|
+
setCurrentSuggestions,
|
|
221
|
+
suggestionsAbortControllerRef,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
currentSuggestions.length == 0 ? 0 : SUGGESTIONS_DEBOUNCE_TIMEOUT,
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return () => {
|
|
229
|
+
clearTimeout(debounceTimerRef.current);
|
|
230
|
+
};
|
|
231
|
+
}, [isLoading, chatSuggestionConfiguration]);
|
|
172
232
|
|
|
173
233
|
const setOpen = (open: boolean) => {
|
|
174
234
|
onSetOpen?.(open);
|
|
175
235
|
setOpenState(open);
|
|
176
236
|
};
|
|
177
237
|
|
|
178
|
-
const sendMessage = async (
|
|
179
|
-
|
|
180
|
-
|
|
238
|
+
const sendMessage = async (messageContent: string) => {
|
|
239
|
+
abortSuggestions();
|
|
240
|
+
setCurrentSuggestions([]);
|
|
241
|
+
onSubmitMessage?.(messageContent);
|
|
242
|
+
const message: Message = {
|
|
181
243
|
id: nanoid(),
|
|
182
|
-
content:
|
|
244
|
+
content: messageContent,
|
|
183
245
|
role: "user",
|
|
184
|
-
}
|
|
246
|
+
};
|
|
247
|
+
append(message);
|
|
248
|
+
return message;
|
|
185
249
|
};
|
|
186
250
|
|
|
251
|
+
const [openState, setOpenState] = React.useState(defaultOpen);
|
|
252
|
+
const [pushToTalkState, setPushToTalkState] = React.useState(false);
|
|
253
|
+
const mediaStreamRef = useRef<MediaStream | null>(null);
|
|
254
|
+
const audioContextRef = useRef<AudioContext | null>(null);
|
|
255
|
+
const mediaRecorderRef = useRef<MediaRecorder | null>(null);
|
|
256
|
+
const [lastMessageIdBeforeAudio, setLastMessageIdBeforeAudio] = useState<string | null>(null);
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (pushToTalkState) {
|
|
260
|
+
console.log("HERE");
|
|
261
|
+
if (!mediaStreamRef.current || !audioContextRef.current) {
|
|
262
|
+
setPushToTalkState(false);
|
|
263
|
+
requestMicAndPlaybackPermission().then((res) => {
|
|
264
|
+
if (res) {
|
|
265
|
+
mediaStreamRef.current = res.stream;
|
|
266
|
+
audioContextRef.current = res.audioContext;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
} else {
|
|
270
|
+
console.log("Recording started");
|
|
271
|
+
const recordedChunks: Blob[] = [];
|
|
272
|
+
|
|
273
|
+
mediaRecorderRef.current = new MediaRecorder(mediaStreamRef.current);
|
|
274
|
+
mediaRecorderRef.current.start(1000);
|
|
275
|
+
mediaRecorderRef.current.ondataavailable = async (event) => {
|
|
276
|
+
console.log("Recorded audio: ", event.data);
|
|
277
|
+
recordedChunks.push(event.data);
|
|
278
|
+
};
|
|
279
|
+
mediaRecorderRef.current.onstop = async () => {
|
|
280
|
+
console.log("Recording stopped");
|
|
281
|
+
const completeBlob = new Blob(recordedChunks, { type: "audio/mp4" });
|
|
282
|
+
|
|
283
|
+
const formData = new FormData();
|
|
284
|
+
formData.append("file", completeBlob, "recording.mp4");
|
|
285
|
+
|
|
286
|
+
const response = await fetch(context.copilotApiConfig.transcribeAudioUrl!, {
|
|
287
|
+
method: "POST",
|
|
288
|
+
body: formData,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
throw new Error(`Error: ${response.statusText}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const transcription = await response.json();
|
|
296
|
+
const message = await sendMessage(transcription.text);
|
|
297
|
+
setLastMessageIdBeforeAudio(message.id);
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
302
|
+
mediaRecorderRef.current.stop();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return () => {
|
|
307
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
308
|
+
mediaRecorderRef.current.stop();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}, [pushToTalkState]);
|
|
312
|
+
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
if (lastMessageIdBeforeAudio && !isLoading) {
|
|
315
|
+
if (audioContextRef.current) {
|
|
316
|
+
const lastMessageIndex = context.messages.findIndex(
|
|
317
|
+
(message) => message.id === lastMessageIdBeforeAudio,
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const messagesAfterLast = context.messages
|
|
321
|
+
.slice(lastMessageIndex + 1)
|
|
322
|
+
.filter((message) => message.role === "assistant" && message.content);
|
|
323
|
+
|
|
324
|
+
const text = messagesAfterLast.map((message) => message.content).join("\n");
|
|
325
|
+
const encodedText = encodeURIComponent(text);
|
|
326
|
+
const url = `${context.copilotApiConfig.textToSpeechUrl}?text=${encodedText}`;
|
|
327
|
+
|
|
328
|
+
fetch(url)
|
|
329
|
+
.then((response) => response.arrayBuffer())
|
|
330
|
+
.then((arrayBuffer) => audioContextRef.current!.decodeAudioData(arrayBuffer))
|
|
331
|
+
.then((audioBuffer) => {
|
|
332
|
+
const source = audioContextRef.current!.createBufferSource();
|
|
333
|
+
source.buffer = audioBuffer;
|
|
334
|
+
source.connect(audioContextRef.current!.destination);
|
|
335
|
+
source.start(0);
|
|
336
|
+
})
|
|
337
|
+
.catch((error) => {
|
|
338
|
+
console.error("Error with decoding audio data", error);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
setLastMessageIdBeforeAudio(null);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}, [isLoading]);
|
|
345
|
+
|
|
187
346
|
return (
|
|
188
|
-
<ChatContextProvider
|
|
347
|
+
<ChatContextProvider
|
|
348
|
+
icons={icons}
|
|
349
|
+
labels={labels}
|
|
350
|
+
open={openState}
|
|
351
|
+
setOpen={setOpenState}
|
|
352
|
+
addChatSuggestionConfiguration={addChatSuggestionConfiguration}
|
|
353
|
+
removeChatSuggestionConfiguration={removeChatSuggestion}
|
|
354
|
+
>
|
|
189
355
|
{children}
|
|
190
356
|
<div className={className}>
|
|
191
|
-
<Button
|
|
357
|
+
<Button
|
|
358
|
+
open={openState}
|
|
359
|
+
setOpen={setOpen}
|
|
360
|
+
pushToTalk={pushToTalkState}
|
|
361
|
+
setPushToTalk={setPushToTalkState}
|
|
362
|
+
></Button>
|
|
192
363
|
<Window
|
|
193
364
|
open={openState}
|
|
194
365
|
setOpen={setOpen}
|
|
@@ -197,12 +368,29 @@ export const CopilotChat = ({
|
|
|
197
368
|
hitEscapeToClose={hitEscapeToClose}
|
|
198
369
|
>
|
|
199
370
|
<Header open={openState} setOpen={setOpen} />
|
|
200
|
-
<Messages messages={visibleMessages} inProgress={isLoading}
|
|
201
|
-
|
|
371
|
+
<Messages messages={visibleMessages} inProgress={isLoading}>
|
|
372
|
+
{currentSuggestions.length > 0 && (
|
|
373
|
+
<div>
|
|
374
|
+
<h6>Suggested:</h6>
|
|
375
|
+
<div className="suggestions">
|
|
376
|
+
{currentSuggestions.map((suggestion, index) => (
|
|
377
|
+
<Suggestion
|
|
378
|
+
key={index}
|
|
379
|
+
title={suggestion.title}
|
|
380
|
+
message={suggestion.message}
|
|
381
|
+
partial={suggestion.partial}
|
|
382
|
+
className={suggestion.className}
|
|
383
|
+
onClick={(message) => sendMessage(message)}
|
|
384
|
+
/>
|
|
385
|
+
))}
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
)}
|
|
202
389
|
{showResponseButton && visibleMessages.length > 0 && (
|
|
203
390
|
<ResponseButton onClick={isLoading ? stop : reload} inProgress={isLoading} />
|
|
204
391
|
)}
|
|
205
|
-
</
|
|
392
|
+
</Messages>
|
|
393
|
+
<Input inProgress={isLoading} onSend={sendMessage} isVisible={openState} />
|
|
206
394
|
</Window>
|
|
207
395
|
</div>
|
|
208
396
|
</ChatContextProvider>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useMemo } from "react";
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
2
|
import * as DefaultIcons from "./Icons";
|
|
3
|
+
import { CopilotChatSuggestion, CopilotChatSuggestionConfiguration } from "../../types/suggestions";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Icons for CopilotChat component.
|
|
@@ -105,6 +106,11 @@ interface ChatContext {
|
|
|
105
106
|
icons: Required<CopilotChatIcons>;
|
|
106
107
|
open: boolean;
|
|
107
108
|
setOpen: (open: boolean) => void;
|
|
109
|
+
addChatSuggestionConfiguration: (
|
|
110
|
+
id: string,
|
|
111
|
+
suggestion: CopilotChatSuggestionConfiguration,
|
|
112
|
+
) => void;
|
|
113
|
+
removeChatSuggestionConfiguration: (id: string) => void;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
export const ChatContext = React.createContext<ChatContext | undefined>(undefined);
|
|
@@ -128,6 +134,11 @@ interface ChatContextProps {
|
|
|
128
134
|
children?: React.ReactNode;
|
|
129
135
|
open: boolean;
|
|
130
136
|
setOpen: (open: boolean) => void;
|
|
137
|
+
addChatSuggestionConfiguration: (
|
|
138
|
+
id: string,
|
|
139
|
+
suggestion: CopilotChatSuggestionConfiguration,
|
|
140
|
+
) => void;
|
|
141
|
+
removeChatSuggestionConfiguration: (id: string) => void;
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
export const ChatContextProvider = ({
|
|
@@ -139,39 +150,40 @@ export const ChatContextProvider = ({
|
|
|
139
150
|
children,
|
|
140
151
|
open,
|
|
141
152
|
setOpen,
|
|
153
|
+
addChatSuggestionConfiguration,
|
|
154
|
+
removeChatSuggestionConfiguration,
|
|
142
155
|
}: ChatContextProps) => {
|
|
143
|
-
const context =
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
regenerateResponse: "Regenerate response",
|
|
154
|
-
},
|
|
155
|
-
...labels,
|
|
156
|
+
const context = {
|
|
157
|
+
labels: {
|
|
158
|
+
...{
|
|
159
|
+
initial: "",
|
|
160
|
+
title: "CopilotKit",
|
|
161
|
+
placeholder: "Type a message...",
|
|
162
|
+
thinking: "Thinking...",
|
|
163
|
+
error: "❌ An error occurred. Please try again.",
|
|
164
|
+
stopGenerating: "Stop generating",
|
|
165
|
+
regenerateResponse: "Regenerate response",
|
|
156
166
|
},
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
...labels,
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
icons: {
|
|
171
|
+
...{
|
|
172
|
+
openIcon: DefaultIcons.OpenIcon,
|
|
173
|
+
closeIcon: DefaultIcons.CloseIcon,
|
|
174
|
+
headerCloseIcon: DefaultIcons.HeaderCloseIcon,
|
|
175
|
+
sendIcon: DefaultIcons.SendIcon,
|
|
176
|
+
activityIcon: DefaultIcons.ActivityIcon,
|
|
177
|
+
spinnerIcon: DefaultIcons.SpinnerIcon,
|
|
178
|
+
stopIcon: DefaultIcons.StopIcon,
|
|
179
|
+
regenerateIcon: DefaultIcons.RegenerateIcon,
|
|
170
180
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
icons,
|
|
182
|
+
},
|
|
183
|
+
open,
|
|
184
|
+
setOpen,
|
|
185
|
+
addChatSuggestionConfiguration,
|
|
186
|
+
removeChatSuggestionConfiguration,
|
|
187
|
+
};
|
|
176
188
|
return <ChatContext.Provider value={context}>{children}</ChatContext.Provider>;
|
|
177
189
|
};
|
|
@@ -92,6 +92,33 @@ export const SpinnerIcon = (
|
|
|
92
92
|
</svg>
|
|
93
93
|
);
|
|
94
94
|
|
|
95
|
+
export const SmallSpinnerIcon = (
|
|
96
|
+
<svg
|
|
97
|
+
style={{
|
|
98
|
+
animation: "copilotKitSpinAnimation 1s linear infinite",
|
|
99
|
+
}}
|
|
100
|
+
width="13"
|
|
101
|
+
height="13"
|
|
102
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
103
|
+
fill="none"
|
|
104
|
+
viewBox="0 0 24 24"
|
|
105
|
+
>
|
|
106
|
+
<circle
|
|
107
|
+
style={{ opacity: 0.25 }}
|
|
108
|
+
cx="12"
|
|
109
|
+
cy="12"
|
|
110
|
+
r="10"
|
|
111
|
+
stroke="currentColor"
|
|
112
|
+
strokeWidth="4"
|
|
113
|
+
></circle>
|
|
114
|
+
<path
|
|
115
|
+
style={{ opacity: 0.75 }}
|
|
116
|
+
fill="currentColor"
|
|
117
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
118
|
+
></path>
|
|
119
|
+
</svg>
|
|
120
|
+
);
|
|
121
|
+
|
|
95
122
|
export const ActivityIcon = (
|
|
96
123
|
<svg
|
|
97
124
|
style={{
|
|
@@ -3,7 +3,7 @@ import { InputProps } from "./props";
|
|
|
3
3
|
import { useChatContext } from "./ChatContext";
|
|
4
4
|
import AutoResizingTextarea from "./Textarea";
|
|
5
5
|
|
|
6
|
-
export const Input = ({ inProgress, onSend,
|
|
6
|
+
export const Input = ({ inProgress, onSend, isVisible = false }: InputProps) => {
|
|
7
7
|
const context = useChatContext();
|
|
8
8
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
9
9
|
|
|
@@ -34,7 +34,6 @@ export const Input = ({ inProgress, onSend, children, isVisible = false }: Input
|
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
36
|
<div className="copilotKitInput" onClick={handleDivClick}>
|
|
37
|
-
<span>{children}</span>
|
|
38
37
|
<button className="copilotKitSendButton" disabled={disabled} onClick={send}>
|
|
39
38
|
{icon}
|
|
40
39
|
</button>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useMemo } from "react";
|
|
2
|
-
import { MessagesProps } from "./props";
|
|
2
|
+
import { MessagesProps, SuggestionsProps } from "./props";
|
|
3
3
|
import { useChatContext } from "./ChatContext";
|
|
4
4
|
import { nanoid } from "nanoid";
|
|
5
5
|
import { Message, decodeResult } from "@copilotkit/shared";
|
|
6
6
|
import { Markdown } from "./Markdown";
|
|
7
7
|
import { ActionRenderProps, RenderFunctionStatus, useCopilotContext } from "@copilotkit/react-core";
|
|
8
8
|
|
|
9
|
-
export const Messages = ({ messages, inProgress }: MessagesProps) => {
|
|
9
|
+
export const Messages = ({ messages, inProgress, children }: MessagesProps) => {
|
|
10
10
|
const { chatComponentsCache } = useCopilotContext();
|
|
11
11
|
const context = useChatContext();
|
|
12
12
|
const initialMessages = useMemo(
|
|
@@ -145,7 +145,7 @@ export const Messages = ({ messages, inProgress }: MessagesProps) => {
|
|
|
145
145
|
);
|
|
146
146
|
}
|
|
147
147
|
})}
|
|
148
|
-
<
|
|
148
|
+
<footer ref={messagesEndRef}>{children}</footer>
|
|
149
149
|
</div>
|
|
150
150
|
);
|
|
151
151
|
};
|
|
@@ -1,13 +1,59 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A chatbot popup component for CopilotKit.
|
|
3
|
+
*
|
|
4
|
+
* <img src="/images/CopilotPopup/CopilotPopup.gif" width="500" />
|
|
5
|
+
*
|
|
6
|
+
* <img referrerPolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=a9b290bb-38f9-4518-ac3b-8f54fdbf43be" />
|
|
7
|
+
*
|
|
8
|
+
* A chatbot popup component for the CopilotKit framework. The component allows for a high degree
|
|
9
|
+
* of customization through various props and custom CSS.
|
|
10
|
+
*
|
|
11
|
+
* See [CopilotSidebar](./CopilotSidebar) for a sidebar version of this component.
|
|
12
|
+
*
|
|
13
|
+
* <RequestExample>
|
|
14
|
+
* ```jsx CopilotPopup Example
|
|
15
|
+
* import { CopilotPopup } from "@copilotkit/react-ui";
|
|
16
|
+
*
|
|
17
|
+
* <CopilotPopup
|
|
18
|
+
* labels={{
|
|
19
|
+
* title: "Your Assistant",
|
|
20
|
+
* initial: "Hi! 👋 How can I assist you today?",
|
|
21
|
+
* }}
|
|
22
|
+
* />
|
|
23
|
+
* ```
|
|
24
|
+
* </RequestExample>
|
|
25
|
+
*
|
|
26
|
+
* ## Custom CSS
|
|
27
|
+
*
|
|
28
|
+
* You can customize the colors of the chat window by overriding the CSS variables
|
|
29
|
+
* defined in the [default styles](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/colors.css).
|
|
30
|
+
*
|
|
31
|
+
* For example, to set the primary color to purple:
|
|
32
|
+
*
|
|
33
|
+
* ```jsx
|
|
34
|
+
* <div style={{ "--copilot-kit-primary-color": "#7D5BA6" }}>
|
|
35
|
+
* <CopilotPopup />
|
|
36
|
+
* </div>
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* To further customize the chat window, you can override the CSS classes defined
|
|
40
|
+
* [here](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/).
|
|
41
|
+
*
|
|
42
|
+
* For example:
|
|
43
|
+
*
|
|
44
|
+
* ```css
|
|
45
|
+
* .copilotKitButton {
|
|
46
|
+
* border-radius: 0;
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
2
50
|
|
|
3
|
-
|
|
4
|
-
children?: React.ReactNode;
|
|
5
|
-
}
|
|
51
|
+
import { CopilotChat, CopilotChatProps } from "./Chat";
|
|
6
52
|
|
|
7
|
-
export
|
|
53
|
+
export function CopilotPopup(props: CopilotChatProps) {
|
|
8
54
|
props = {
|
|
9
55
|
...props,
|
|
10
56
|
className: props.className ? props.className + " copilotKitPopup" : "copilotKitPopup",
|
|
11
57
|
};
|
|
12
58
|
return <CopilotChat {...props}>{props.children}</CopilotChat>;
|
|
13
|
-
}
|
|
59
|
+
}
|
|
@@ -1,11 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A chatbot sidebar component for CopilotKit.
|
|
3
|
+
*
|
|
4
|
+
* <img src="/images/CopilotSidebar/CopilotSidebar.gif" width="500" />
|
|
5
|
+
*
|
|
6
|
+
* <img referrerPolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=a9b290bb-38f9-4518-ac3b-8f54fdbf43be" />
|
|
7
|
+
*
|
|
8
|
+
* A chatbot sidebar component for the CopilotKit framework. Highly customizable through various props and custom CSS.
|
|
9
|
+
*
|
|
10
|
+
* <RequestExample>
|
|
11
|
+
* ```jsx CopilotSidebar Example
|
|
12
|
+
* import { CopilotSidebar } from "@copilotkit/react-ui";
|
|
13
|
+
*
|
|
14
|
+
* <CopilotSidebar
|
|
15
|
+
* labels={{
|
|
16
|
+
* title: "Your Assistant",
|
|
17
|
+
* initial: "Hi! 👋 How can I assist you today?",
|
|
18
|
+
* }}
|
|
19
|
+
* >
|
|
20
|
+
* <YourApp/>
|
|
21
|
+
* </CopilotSidebar>
|
|
22
|
+
* ```
|
|
23
|
+
* </RequestExample>
|
|
24
|
+
*
|
|
25
|
+
*
|
|
26
|
+
* See [CopilotPopup](./CopilotPopup) for a popup version of this component.
|
|
27
|
+
*
|
|
28
|
+
* <Note>
|
|
29
|
+
* To make the sidebar push your content to the side, wrap your content in the
|
|
30
|
+
* sidebar component. If you want the sidebar to overlay your content, place the
|
|
31
|
+
* sidebar component outside of your content.
|
|
32
|
+
* </Note>
|
|
33
|
+
*
|
|
34
|
+
* ## Custom CSS
|
|
35
|
+
*
|
|
36
|
+
* You can customize the colors of the chat window by overriding the CSS variables
|
|
37
|
+
* defined in the [default styles](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/colors.css).
|
|
38
|
+
*
|
|
39
|
+
* For example, to set the primary color to purple:
|
|
40
|
+
*
|
|
41
|
+
* ```jsx
|
|
42
|
+
* <div style={{ "--copilot-kit-primary-color": "#7D5BA6" }}>
|
|
43
|
+
* <CopilotSidebar />
|
|
44
|
+
* </div>
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* To further customize the chat window, you can override the CSS classes defined
|
|
48
|
+
* [here](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/).
|
|
49
|
+
*
|
|
50
|
+
* For example:
|
|
51
|
+
*
|
|
52
|
+
* ```css
|
|
53
|
+
* .copilotKitButton {
|
|
54
|
+
* border-radius: 0;
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
1
58
|
import React, { useState } from "react";
|
|
2
59
|
import { CopilotChat, CopilotChatProps } from "./Chat";
|
|
3
60
|
|
|
4
|
-
|
|
5
|
-
children?: React.ReactNode;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const CopilotSidebar = (props: CopilotSidebarProps) => {
|
|
61
|
+
export function CopilotSidebar(props: CopilotChatProps) {
|
|
9
62
|
props = {
|
|
10
63
|
...props,
|
|
11
64
|
className: props.className ? props.className + " copilotKitSidebar" : "copilotKitSidebar",
|
|
@@ -26,4 +79,4 @@ export const CopilotSidebar = (props: CopilotSidebarProps) => {
|
|
|
26
79
|
</CopilotChat>
|
|
27
80
|
</div>
|
|
28
81
|
);
|
|
29
|
-
}
|
|
82
|
+
}
|