@copilotkit/react-ui 0.36.0-mme-push-to-talk.0 → 0.36.0-mme-pre.1

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 (159) hide show
  1. package/.turbo/turbo-build.log +174 -167
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-DMAQBCTX.mjs → chunk-4MKP23AD.mjs} +6 -4
  4. package/dist/chunk-4MKP23AD.mjs.map +1 -0
  5. package/dist/{chunk-UVMROYDT.mjs → chunk-6XLZXLM5.mjs} +5 -5
  6. package/dist/{chunk-UVMROYDT.mjs.map → chunk-6XLZXLM5.mjs.map} +1 -1
  7. package/dist/{chunk-XYM43AHP.mjs → chunk-7FES2IQA.mjs} +3 -3
  8. package/dist/chunk-ANO23V2M.mjs +135 -0
  9. package/dist/chunk-ANO23V2M.mjs.map +1 -0
  10. package/dist/{chunk-DRNCOXZO.mjs → chunk-BL65ZC6L.mjs} +28 -7
  11. package/dist/chunk-BL65ZC6L.mjs.map +1 -0
  12. package/dist/{chunk-PGZDQT74.mjs → chunk-CE7PJAAO.mjs} +2 -2
  13. package/dist/{chunk-FWTPMPSN.mjs → chunk-FZC7X5PK.mjs} +23 -3
  14. package/dist/{chunk-FWTPMPSN.mjs.map → chunk-FZC7X5PK.mjs.map} +1 -1
  15. package/dist/{chunk-Z45ZEXJW.mjs → chunk-LTCJCXCP.mjs} +5 -8
  16. package/dist/chunk-LTCJCXCP.mjs.map +1 -0
  17. package/dist/chunk-MRFF7GSQ.mjs +1 -0
  18. package/dist/{chunk-SKC7AJIV.mjs → chunk-MRXNTQOX.mjs} +1 -3
  19. package/dist/{chunk-MWFHYCQB.mjs → chunk-PAQWLSA4.mjs} +2 -2
  20. package/dist/chunk-RT2XG2T7.mjs +25 -0
  21. package/dist/chunk-RT2XG2T7.mjs.map +1 -0
  22. package/dist/chunk-T3JTSIHT.mjs +93 -0
  23. package/dist/chunk-T3JTSIHT.mjs.map +1 -0
  24. package/dist/{chunk-A7J4KGLP.mjs → chunk-UPTB2MVO.mjs} +2 -2
  25. package/dist/{chunk-KZME7C5S.mjs → chunk-VUZA5AFH.mjs} +8 -11
  26. package/dist/chunk-VUZA5AFH.mjs.map +1 -0
  27. package/dist/{chunk-XWWMYJJF.mjs → chunk-XRODMID5.mjs} +5 -5
  28. package/dist/{chunk-XWWMYJJF.mjs.map → chunk-XRODMID5.mjs.map} +1 -1
  29. package/dist/{chunk-ZKLK3M77.mjs → chunk-YQ3D5IQV.mjs} +3 -3
  30. package/dist/{chunk-WM6BS77F.mjs → chunk-YQFVRDNC.mjs} +2 -2
  31. package/dist/{chunk-WM6BS77F.mjs.map → chunk-YQFVRDNC.mjs.map} +1 -1
  32. package/dist/chunk-ZO3GLN23.mjs +137 -0
  33. package/dist/chunk-ZO3GLN23.mjs.map +1 -0
  34. package/dist/components/chat/Button.d.ts +1 -1
  35. package/dist/components/chat/Button.js +2 -30
  36. package/dist/components/chat/Button.js.map +1 -1
  37. package/dist/components/chat/Button.mjs +4 -4
  38. package/dist/components/chat/Chat.d.ts +66 -47
  39. package/dist/components/chat/Chat.js +274 -430
  40. package/dist/components/chat/Chat.js.map +1 -1
  41. package/dist/components/chat/Chat.mjs +16 -17
  42. package/dist/components/chat/ChatContext.d.ts +17 -22
  43. package/dist/components/chat/ChatContext.js +23 -8
  44. package/dist/components/chat/ChatContext.js.map +1 -1
  45. package/dist/components/chat/ChatContext.mjs +3 -3
  46. package/dist/components/chat/CodeBlock.js.map +1 -1
  47. package/dist/components/chat/CodeBlock.mjs +3 -3
  48. package/dist/components/chat/Header.js.map +1 -1
  49. package/dist/components/chat/Header.mjs +4 -4
  50. package/dist/components/chat/Icons.d.ts +6 -5
  51. package/dist/components/chat/Icons.js +21 -0
  52. package/dist/components/chat/Icons.js.map +1 -1
  53. package/dist/components/chat/Icons.mjs +4 -2
  54. package/dist/components/chat/Input.js +147 -9
  55. package/dist/components/chat/Input.js.map +1 -1
  56. package/dist/components/chat/Input.mjs +6 -5
  57. package/dist/components/chat/Markdown.js.map +1 -1
  58. package/dist/components/chat/Markdown.mjs +4 -4
  59. package/dist/components/chat/Messages.js.map +1 -1
  60. package/dist/components/chat/Messages.mjs +6 -6
  61. package/dist/components/chat/Modal.d.ts +50 -0
  62. package/dist/components/chat/Modal.js +1584 -0
  63. package/dist/components/chat/Modal.js.map +1 -0
  64. package/dist/components/chat/Modal.mjs +23 -0
  65. package/dist/components/chat/Popup.d.ts +6 -5
  66. package/dist/components/chat/Popup.js +288 -249
  67. package/dist/components/chat/Popup.js.map +1 -1
  68. package/dist/components/chat/Popup.mjs +16 -15
  69. package/dist/components/chat/Response.js.map +1 -1
  70. package/dist/components/chat/Response.mjs +4 -4
  71. package/dist/components/chat/Sidebar.d.ts +6 -5
  72. package/dist/components/chat/Sidebar.js +290 -251
  73. package/dist/components/chat/Sidebar.js.map +1 -1
  74. package/dist/components/chat/Sidebar.mjs +16 -15
  75. package/dist/components/chat/Suggestion.d.ts +1 -2
  76. package/dist/components/chat/Suggestion.js.map +1 -1
  77. package/dist/components/chat/Suggestion.mjs +3 -3
  78. package/dist/components/chat/Textarea.d.ts +4 -4
  79. package/dist/components/chat/Textarea.js +1 -1
  80. package/dist/components/chat/Textarea.js.map +1 -1
  81. package/dist/components/chat/Textarea.mjs +2 -2
  82. package/dist/components/chat/Window.mjs +1 -1
  83. package/dist/components/chat/index.d.ts +2 -1
  84. package/dist/components/chat/index.js +294 -253
  85. package/dist/components/chat/index.js.map +1 -1
  86. package/dist/components/chat/index.mjs +23 -19
  87. package/dist/components/chat/props.d.ts +1 -3
  88. package/dist/components/chat/props.js.map +1 -1
  89. package/dist/components/index.d.ts +2 -1
  90. package/dist/components/index.js +294 -253
  91. package/dist/components/index.js.map +1 -1
  92. package/dist/components/index.mjs +23 -19
  93. package/dist/hooks/index.d.ts +0 -1
  94. package/dist/hooks/index.js +6 -31
  95. package/dist/hooks/index.js.map +1 -1
  96. package/dist/hooks/index.mjs +2 -22
  97. package/dist/hooks/use-copilot-chat-suggestions.d.ts +26 -4
  98. package/dist/hooks/use-copilot-chat-suggestions.js +6 -31
  99. package/dist/hooks/use-copilot-chat-suggestions.js.map +1 -1
  100. package/dist/hooks/use-copilot-chat-suggestions.mjs +2 -22
  101. package/dist/hooks/use-copy-to-clipboard.mjs +1 -1
  102. package/dist/hooks/use-push-to-talk.d.ts +19 -0
  103. package/dist/hooks/use-push-to-talk.js +177 -0
  104. package/dist/hooks/use-push-to-talk.js.map +1 -0
  105. package/dist/hooks/use-push-to-talk.mjs +12 -0
  106. package/dist/hooks/use-push-to-talk.mjs.map +1 -0
  107. package/dist/index.css +60 -8
  108. package/dist/index.css.map +1 -1
  109. package/dist/index.d.ts +2 -1
  110. package/dist/index.js +300 -258
  111. package/dist/index.js.map +1 -1
  112. package/dist/index.mjs +28 -24
  113. package/dist/lib/utils.mjs +1 -1
  114. package/dist/types/suggestions.d.ts +1 -21
  115. package/dist/types/suggestions.js.map +1 -1
  116. package/package.json +6 -6
  117. package/src/components/chat/Button.tsx +2 -35
  118. package/src/components/chat/Chat.tsx +126 -255
  119. package/src/components/chat/ChatContext.tsx +8 -22
  120. package/src/components/chat/Icons.tsx +17 -0
  121. package/src/components/chat/Input.tsx +37 -5
  122. package/src/components/chat/Modal.tsx +115 -0
  123. package/src/components/chat/Popup.tsx +3 -3
  124. package/src/components/chat/Sidebar.tsx +4 -4
  125. package/src/components/chat/Suggestion.tsx +6 -2
  126. package/src/components/chat/Textarea.tsx +1 -1
  127. package/src/components/chat/index.tsx +1 -0
  128. package/src/components/chat/props.ts +1 -3
  129. package/src/css/input.css +18 -8
  130. package/src/css/panel.css +38 -0
  131. package/src/css/window.css +3 -1
  132. package/src/hooks/use-copilot-chat-suggestions.tsx +31 -5
  133. package/src/hooks/use-push-to-talk.tsx +162 -0
  134. package/src/styles.css +1 -0
  135. package/src/types/suggestions.ts +0 -24
  136. package/dist/chunk-5ASYNEHX.mjs +0 -53
  137. package/dist/chunk-5ASYNEHX.mjs.map +0 -1
  138. package/dist/chunk-DMAQBCTX.mjs.map +0 -1
  139. package/dist/chunk-DRNCOXZO.mjs.map +0 -1
  140. package/dist/chunk-JPX5ODUX.mjs +0 -266
  141. package/dist/chunk-JPX5ODUX.mjs.map +0 -1
  142. package/dist/chunk-KZME7C5S.mjs.map +0 -1
  143. package/dist/chunk-PEDSZYHE.mjs +0 -36
  144. package/dist/chunk-PEDSZYHE.mjs.map +0 -1
  145. package/dist/chunk-UGQQ4WEQ.mjs +0 -1
  146. package/dist/chunk-Z45ZEXJW.mjs.map +0 -1
  147. package/dist/components/chat/audio.d.ts +0 -7
  148. package/dist/components/chat/audio.js +0 -77
  149. package/dist/components/chat/audio.js.map +0 -1
  150. package/dist/components/chat/audio.mjs +0 -10
  151. package/src/components/chat/audio.ts +0 -26
  152. /package/dist/{chunk-XYM43AHP.mjs.map → chunk-7FES2IQA.mjs.map} +0 -0
  153. /package/dist/{chunk-PGZDQT74.mjs.map → chunk-CE7PJAAO.mjs.map} +0 -0
  154. /package/dist/{chunk-SKC7AJIV.mjs.map → chunk-MRFF7GSQ.mjs.map} +0 -0
  155. /package/dist/{chunk-UGQQ4WEQ.mjs.map → chunk-MRXNTQOX.mjs.map} +0 -0
  156. /package/dist/{chunk-MWFHYCQB.mjs.map → chunk-PAQWLSA4.mjs.map} +0 -0
  157. /package/dist/{chunk-A7J4KGLP.mjs.map → chunk-UPTB2MVO.mjs.map} +0 -0
  158. /package/dist/{chunk-ZKLK3M77.mjs.map → chunk-YQ3D5IQV.mjs.map} +0 -0
  159. /package/dist/components/chat/{audio.mjs.map → Modal.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/suggestions.ts"],"sourcesContent":["export interface CopilotChatSuggestionConfiguration {\n /**\n * A prompt or instructions for the GPT to generate suggestions.\n */\n instructions: string;\n\n /**\n * The minimum number of suggestions to generate. Defaults to `1`.\n * @default 1\n */\n minSuggestions?: number;\n\n /**\n * The maximum number of suggestions to generate. Defaults to `3`.\n * @default 1\n */\n maxSuggestions?: number;\n\n /**\n * An optional class name to apply to the suggestions.\n */\n className?: string;\n}\n\nexport interface CopilotChatSuggestion {\n title: string;\n message: string;\n partial?: boolean;\n className?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../../src/types/suggestions.ts"],"sourcesContent":["export interface CopilotChatSuggestion {\n title: string;\n message: string;\n partial?: boolean;\n className?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.36.0-mme-push-to-talk.0",
7
+ "version": "0.36.0-mme-pre.1",
8
8
  "sideEffects": [
9
9
  "**/*.css"
10
10
  ],
@@ -35,9 +35,9 @@
35
35
  "ts-jest": "^29.1.1",
36
36
  "tsup": "^6.7.0",
37
37
  "typescript": "^5.2.3",
38
- "eslint-config-custom": "0.11.0-mme-push-to-talk.0",
39
- "tailwind-config": "0.10.0-mme-push-to-talk.0",
40
- "tsconfig": "0.15.0-mme-push-to-talk.0"
38
+ "eslint-config-custom": "0.11.0-mme-pre.1",
39
+ "tailwind-config": "0.10.0-mme-pre.1",
40
+ "tsconfig": "0.15.0-mme-pre.1"
41
41
  },
42
42
  "dependencies": {
43
43
  "nanoid": "^4.0.2",
@@ -45,8 +45,8 @@
45
45
  "react-syntax-highlighter": "^15.5.0",
46
46
  "remark-gfm": "^3.0.1",
47
47
  "remark-math": "^5.1.1",
48
- "@copilotkit/shared": "0.36.0-mme-push-to-talk.0",
49
- "@copilotkit/react-core": "0.36.0-mme-push-to-talk.0"
48
+ "@copilotkit/shared": "0.36.0-mme-pre.1",
49
+ "@copilotkit/react-core": "0.36.0-mme-pre.1"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsup --clean",
@@ -1,44 +1,11 @@
1
- import { useEffect, useRef, useState } from "react";
2
1
  import { ButtonProps } from "./props";
3
2
  import { useChatContext } from "./ChatContext";
4
3
 
5
- export const Button = ({ open, setOpen, pushToTalk, setPushToTalk }: ButtonProps) => {
4
+ export const Button = ({ open, setOpen }: ButtonProps) => {
6
5
  const context = useChatContext();
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
6
 
40
7
  return (
41
- <div onClick={handleClick} onMouseDown={handleMouseDown}>
8
+ <div onClick={() => setOpen(!open)}>
42
9
  <button
43
10
  className={`copilotKitButton ${open ? "open" : ""}`}
44
11
  aria-label={open ? "Close Chat" : "Open Chat"}
@@ -1,31 +1,66 @@
1
- import React, { useEffect, useRef, useState } from "react";
2
- import { CopilotChatIcons, ChatContextProvider, CopilotChatLabels } from "./ChatContext";
3
- import {
4
- SystemMessageFunction,
5
- extract,
6
- useCopilotChat,
7
- useCopilotContext,
8
- } from "@copilotkit/react-core";
1
+ /**
2
+ * An embeddable chat panel for CopilotKit.
3
+ *
4
+ * <img src="/images/CopilotChat/CopilotChat.gif" width="500" />
5
+ *
6
+ * A chatbot panel component for the CopilotKit framework. The component allows for a high degree
7
+ * of customization through various props and custom CSS.
8
+ *
9
+ * <RequestExample>
10
+ * ```jsx CopilotChat Example
11
+ * import { CopilotChat } from "@copilotkit/react-ui";
12
+ *
13
+ * <CopilotChat
14
+ * labels={{
15
+ * title: "Your Assistant",
16
+ * initial: "Hi! 👋 How can I assist you today?",
17
+ * }}
18
+ * />
19
+ * ```
20
+ * </RequestExample>
21
+ *
22
+ * ## Custom CSS
23
+ *
24
+ * You can customize the colors of the panel by overriding the CSS variables
25
+ * defined in the [default styles](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/colors.css).
26
+ *
27
+ * For example, to set the primary color to purple:
28
+ *
29
+ * ```jsx
30
+ * <div style={{ "--copilot-kit-primary-color": "#7D5BA6" }}>
31
+ * <CopilotPopup />
32
+ * </div>
33
+ * ```
34
+ *
35
+ * To further customize the panel, you can override the CSS classes defined
36
+ * [here](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/).
37
+ *
38
+ * For example:
39
+ *
40
+ * ```css
41
+ * .copilotKitButton {
42
+ * border-radius: 0;
43
+ * }
44
+ * ```
45
+ */
46
+
9
47
  import {
10
- ButtonProps,
11
- HeaderProps,
12
- WindowProps,
13
- MessagesProps,
14
- InputProps,
15
- ResponseButtonProps,
16
- SuggestionsProps,
17
- } from "./props";
18
- import { Window as DefaultWindow } from "./Window";
19
- import { Button as DefaultButton } from "./Button";
20
- import { Header as DefaultHeader } from "./Header";
48
+ ChatContext,
49
+ ChatContextProvider,
50
+ CopilotChatIcons,
51
+ CopilotChatLabels,
52
+ } from "./ChatContext";
21
53
  import { Messages as DefaultMessages } from "./Messages";
22
54
  import { Input as DefaultInput } from "./Input";
23
- import { nanoid } from "nanoid";
24
55
  import { ResponseButton as DefaultResponseButton } from "./Response";
25
- import { Suggestion, reloadSuggestions } from "./Suggestion";
26
- import { CopilotChatSuggestion, CopilotChatSuggestionConfiguration } from "../../types/suggestions";
27
- import { requestMicAndPlaybackPermission } from "./audio";
56
+ import { Suggestion } from "./Suggestion";
57
+ import React, { useEffect, useRef, useState } from "react";
58
+ import { SystemMessageFunction, useCopilotChat, useCopilotContext } from "@copilotkit/react-core";
59
+ import { nanoid } from "nanoid";
60
+ import { reloadSuggestions } from "./Suggestion";
61
+ import { CopilotChatSuggestion } from "../../types/suggestions";
28
62
  import { Message } from "@copilotkit/shared";
63
+ import { InputProps, MessagesProps, ResponseButtonProps } from "./props";
29
64
 
30
65
  /**
31
66
  * Props for CopilotChat component.
@@ -41,29 +76,6 @@ export interface CopilotChatProps {
41
76
  */
42
77
  instructions?: string;
43
78
 
44
- /**
45
- * Whether the chat window should be open by default.
46
- * @default false
47
- */
48
- defaultOpen?: boolean;
49
-
50
- /**
51
- * If the chat window should close when the user clicks outside of it.
52
- * @default true
53
- */
54
- clickOutsideToClose?: boolean;
55
-
56
- /**
57
- * If the chat window should close when the user hits the Escape key.
58
- * @default true
59
- */
60
- hitEscapeToClose?: boolean;
61
-
62
- /**
63
- * A callback that gets called when the chat window opens or closes.
64
- */
65
- onSetOpen?: (open: boolean) => void;
66
-
67
79
  /**
68
80
  * A callback that gets called when the in progress state changes.
69
81
  */
@@ -74,13 +86,6 @@ export interface CopilotChatProps {
74
86
  */
75
87
  onSubmitMessage?: (message: string) => void;
76
88
 
77
- /**
78
- * The shortcut key to open the chat window.
79
- * Uses Command-[shortcut] on a Mac and Ctrl-[shortcut] on Windows.
80
- * @default "/"
81
- */
82
- shortcut?: string;
83
-
84
89
  /**
85
90
  * Icons can be used to set custom icons for the chat window.
86
91
  */
@@ -105,21 +110,6 @@ export interface CopilotChatProps {
105
110
  */
106
111
  showResponseButton?: boolean;
107
112
 
108
- /**
109
- * A custom Window component to use instead of the default.
110
- */
111
- Window?: React.ComponentType<WindowProps>;
112
-
113
- /**
114
- * A custom Button component to use instead of the default.
115
- */
116
- Button?: React.ComponentType<ButtonProps>;
117
-
118
- /**
119
- * A custom Header component to use instead of the default.
120
- */
121
- Header?: React.ComponentType<HeaderProps>;
122
-
123
113
  /**
124
114
  * A custom Messages component to use instead of the default.
125
115
  */
@@ -146,37 +136,78 @@ export interface CopilotChatProps {
146
136
  children?: React.ReactNode;
147
137
  }
148
138
 
149
- const SUGGESTIONS_DEBOUNCE_TIMEOUT = 1000;
150
-
151
- export const CopilotChat = ({
139
+ export function CopilotChat({
152
140
  instructions,
153
- defaultOpen = false,
154
- clickOutsideToClose = true,
155
- hitEscapeToClose = true,
156
- onSetOpen,
157
141
  onSubmitMessage,
158
- shortcut = "/",
159
142
  icons,
160
143
  labels,
161
144
  makeSystemMessage,
162
145
  showResponseButton = true,
163
146
  onInProgress,
164
- Window = DefaultWindow,
165
- Button = DefaultButton,
166
- Header = DefaultHeader,
167
147
  Messages = DefaultMessages,
168
148
  Input = DefaultInput,
169
149
  ResponseButton = DefaultResponseButton,
170
150
  className,
171
- children,
172
- }: CopilotChatProps) => {
151
+ }: CopilotChatProps) {
152
+ const { visibleMessages, isLoading, currentSuggestions, sendMessage, stop, reload } =
153
+ useCopilotChatLogic(instructions, makeSystemMessage, onInProgress, onSubmitMessage);
154
+
155
+ const context = React.useContext(ChatContext);
156
+ let open = true;
157
+ let setOpen: (open: boolean) => void = () => {};
158
+ if (context) {
159
+ icons = context.icons;
160
+ labels = context.labels;
161
+ open = context.open;
162
+ setOpen = context.setOpen;
163
+ }
164
+
165
+ return (
166
+ <ChatContextProvider icons={icons} labels={labels} open={open} setOpen={setOpen}>
167
+ <div className={`copilotKitPanel ${className}`}>
168
+ <Messages messages={visibleMessages} inProgress={isLoading}>
169
+ {currentSuggestions.length > 0 && (
170
+ <div>
171
+ <h6>Suggested:</h6>
172
+ <div className="suggestions">
173
+ {currentSuggestions.map((suggestion, index) => (
174
+ <Suggestion
175
+ key={index}
176
+ title={suggestion.title}
177
+ message={suggestion.message}
178
+ partial={suggestion.partial}
179
+ className={suggestion.className}
180
+ onClick={(message) => sendMessage(message)}
181
+ />
182
+ ))}
183
+ </div>
184
+ </div>
185
+ )}
186
+ {showResponseButton && visibleMessages.length > 0 && (
187
+ <ResponseButton onClick={isLoading ? stop : reload} inProgress={isLoading} />
188
+ )}
189
+ </Messages>
190
+ <Input inProgress={isLoading} onSend={sendMessage} isVisible={true} />
191
+ </div>
192
+ </ChatContextProvider>
193
+ );
194
+ }
195
+
196
+ const SUGGESTIONS_DEBOUNCE_TIMEOUT = 1000;
197
+
198
+ export const useCopilotChatLogic = (
199
+ instructions?: string,
200
+ makeSystemMessage?: SystemMessageFunction,
201
+ onInProgress?: (isLoading: boolean) => void,
202
+ onSubmitMessage?: (messageContent: string) => void,
203
+ ) => {
173
204
  const { visibleMessages, append, reload, stop, isLoading, input, setInput } = useCopilotChat({
174
205
  id: nanoid(),
175
206
  makeSystemMessage,
176
207
  additionalInstructions: instructions,
177
208
  });
178
209
 
179
- const [currentSuggestions, setCurrentSuggestions] = React.useState<CopilotChatSuggestion[]>([]);
210
+ const [currentSuggestions, setCurrentSuggestions] = useState<CopilotChatSuggestion[]>([]);
180
211
  const suggestionsAbortControllerRef = useRef<AbortController | null>(null);
181
212
  const debounceTimerRef = useRef<any>();
182
213
 
@@ -187,24 +218,6 @@ export const CopilotChat = ({
187
218
 
188
219
  const context = useCopilotContext();
189
220
 
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
-
208
221
  useEffect(() => {
209
222
  onInProgress?.(isLoading);
210
223
 
@@ -212,11 +225,11 @@ export const CopilotChat = ({
212
225
 
213
226
  debounceTimerRef.current = setTimeout(
214
227
  () => {
215
- if (!isLoading && Object.keys(chatSuggestionConfiguration).length !== 0) {
228
+ if (!isLoading && Object.keys(context.chatSuggestionConfiguration).length !== 0) {
216
229
  suggestionsAbortControllerRef.current = new AbortController();
217
230
  reloadSuggestions(
218
231
  context,
219
- chatSuggestionConfiguration,
232
+ context.chatSuggestionConfiguration,
220
233
  setCurrentSuggestions,
221
234
  suggestionsAbortControllerRef,
222
235
  );
@@ -228,12 +241,7 @@ export const CopilotChat = ({
228
241
  return () => {
229
242
  clearTimeout(debounceTimerRef.current);
230
243
  };
231
- }, [isLoading, chatSuggestionConfiguration]);
232
-
233
- const setOpen = (open: boolean) => {
234
- onSetOpen?.(open);
235
- setOpenState(open);
236
- };
244
+ }, [isLoading, context.chatSuggestionConfiguration]);
237
245
 
238
246
  const sendMessage = async (messageContent: string) => {
239
247
  abortSuggestions();
@@ -248,151 +256,14 @@ export const CopilotChat = ({
248
256
  return message;
249
257
  };
250
258
 
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
-
346
- return (
347
- <ChatContextProvider
348
- icons={icons}
349
- labels={labels}
350
- open={openState}
351
- setOpen={setOpenState}
352
- addChatSuggestionConfiguration={addChatSuggestionConfiguration}
353
- removeChatSuggestionConfiguration={removeChatSuggestion}
354
- >
355
- {children}
356
- <div className={className}>
357
- <Button
358
- open={openState}
359
- setOpen={setOpen}
360
- pushToTalk={pushToTalkState}
361
- setPushToTalk={setPushToTalkState}
362
- ></Button>
363
- <Window
364
- open={openState}
365
- setOpen={setOpen}
366
- clickOutsideToClose={clickOutsideToClose}
367
- shortcut={shortcut}
368
- hitEscapeToClose={hitEscapeToClose}
369
- >
370
- <Header open={openState} setOpen={setOpen} />
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
- )}
389
- {showResponseButton && visibleMessages.length > 0 && (
390
- <ResponseButton onClick={isLoading ? stop : reload} inProgress={isLoading} />
391
- )}
392
- </Messages>
393
- <Input inProgress={isLoading} onSend={sendMessage} isVisible={openState} />
394
- </Window>
395
- </div>
396
- </ChatContextProvider>
397
- );
259
+ return {
260
+ visibleMessages,
261
+ isLoading,
262
+ currentSuggestions,
263
+ sendMessage,
264
+ stop,
265
+ reload,
266
+ input,
267
+ setInput,
268
+ };
398
269
  };
@@ -1,6 +1,5 @@
1
1
  import React, { useMemo, useState } from "react";
2
2
  import * as DefaultIcons from "./Icons";
3
- import { CopilotChatSuggestion, CopilotChatSuggestionConfiguration } from "../../types/suggestions";
4
3
 
5
4
  /**
6
5
  * Icons for CopilotChat component.
@@ -53,6 +52,13 @@ export interface CopilotChatIcons {
53
52
  * @default <RegenerateIcon />
54
53
  */
55
54
  regenerateIcon?: React.ReactNode;
55
+
56
+ /**
57
+ * The icons to use for push to talk.
58
+ * @default <PushToTalkIcon />
59
+ */
60
+
61
+ pushToTalkIcon?: React.ReactNode;
56
62
  }
57
63
 
58
64
  /**
@@ -76,12 +82,6 @@ export interface CopilotChatLabels {
76
82
  */
77
83
  placeholder?: string;
78
84
 
79
- /**
80
- * The message to display while the chat GPT is "thinking".
81
- * @default "Thinking..."
82
- */
83
- thinking?: string;
84
-
85
85
  /**
86
86
  * The message to display when an error occurs.
87
87
  * @default "❌ An error occurred. Please try again."
@@ -106,11 +106,6 @@ interface ChatContext {
106
106
  icons: Required<CopilotChatIcons>;
107
107
  open: boolean;
108
108
  setOpen: (open: boolean) => void;
109
- addChatSuggestionConfiguration: (
110
- id: string,
111
- suggestion: CopilotChatSuggestionConfiguration,
112
- ) => void;
113
- removeChatSuggestionConfiguration: (id: string) => void;
114
109
  }
115
110
 
116
111
  export const ChatContext = React.createContext<ChatContext | undefined>(undefined);
@@ -134,11 +129,6 @@ interface ChatContextProps {
134
129
  children?: React.ReactNode;
135
130
  open: boolean;
136
131
  setOpen: (open: boolean) => void;
137
- addChatSuggestionConfiguration: (
138
- id: string,
139
- suggestion: CopilotChatSuggestionConfiguration,
140
- ) => void;
141
- removeChatSuggestionConfiguration: (id: string) => void;
142
132
  }
143
133
 
144
134
  export const ChatContextProvider = ({
@@ -150,8 +140,6 @@ export const ChatContextProvider = ({
150
140
  children,
151
141
  open,
152
142
  setOpen,
153
- addChatSuggestionConfiguration,
154
- removeChatSuggestionConfiguration,
155
143
  }: ChatContextProps) => {
156
144
  const context = {
157
145
  labels: {
@@ -159,7 +147,6 @@ export const ChatContextProvider = ({
159
147
  initial: "",
160
148
  title: "CopilotKit",
161
149
  placeholder: "Type a message...",
162
- thinking: "Thinking...",
163
150
  error: "❌ An error occurred. Please try again.",
164
151
  stopGenerating: "Stop generating",
165
152
  regenerateResponse: "Regenerate response",
@@ -177,13 +164,12 @@ export const ChatContextProvider = ({
177
164
  spinnerIcon: DefaultIcons.SpinnerIcon,
178
165
  stopIcon: DefaultIcons.StopIcon,
179
166
  regenerateIcon: DefaultIcons.RegenerateIcon,
167
+ pushToTalkIcon: DefaultIcons.PushToTalkIcon,
180
168
  },
181
169
  icons,
182
170
  },
183
171
  open,
184
172
  setOpen,
185
- addChatSuggestionConfiguration,
186
- removeChatSuggestionConfiguration,
187
173
  };
188
174
  return <ChatContext.Provider value={context}>{children}</ChatContext.Provider>;
189
175
  };
@@ -204,3 +204,20 @@ export const RegenerateIcon = (
204
204
  <path d="M197.67 186.37a8 8 0 0 1 0 11.29C196.58 198.73 170.82 224 128 224c-37.39 0-64.53-22.4-80-39.85V208a8 8 0 0 1-16 0v-48a8 8 0 0 1 8-8h48a8 8 0 0 1 0 16H55.44C67.76 183.35 93 208 128 208c36 0 58.14-21.46 58.36-21.68a8 8 0 0 1 11.31.05ZM216 40a8 8 0 0 0-8 8v23.85C192.53 54.4 165.39 32 128 32c-42.82 0-68.58 25.27-69.66 26.34a8 8 0 0 0 11.3 11.34C69.86 69.46 92 48 128 48c35 0 60.24 24.65 72.56 40H168a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8V48a8 8 0 0 0-8-8Z" />
205
205
  </svg>
206
206
  );
207
+
208
+ export const PushToTalkIcon = (
209
+ <svg
210
+ xmlns="http://www.w3.org/2000/svg"
211
+ fill="none"
212
+ viewBox="0 0 24 24"
213
+ strokeWidth={1.5}
214
+ stroke="currentColor"
215
+ className="w-6 h-6"
216
+ >
217
+ <path
218
+ strokeLinecap="round"
219
+ strokeLinejoin="round"
220
+ d="M12 18.75a6 6 0 0 0 6-6v-1.5m-6 7.5a6 6 0 0 1-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 0 1-3-3V4.5a3 3 0 1 1 6 0v8.25a3 3 0 0 1-3 3Z"
221
+ />
222
+ </svg>
223
+ );