@copilotkit/react-ui 1.9.3 → 1.10.0-next.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 (198) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/{chunk-GVKA7RQQ.mjs → chunk-7CAK2CNK.mjs} +2 -2
  3. package/dist/{chunk-KN2GCKBE.mjs → chunk-7RNOT3GM.mjs} +9 -9
  4. package/dist/{chunk-KENCH7RN.mjs → chunk-B5IFB5YJ.mjs} +1 -1
  5. package/dist/chunk-B5IFB5YJ.mjs.map +1 -0
  6. package/dist/chunk-DBKRAOH7.mjs +34 -0
  7. package/dist/chunk-DBKRAOH7.mjs.map +1 -0
  8. package/dist/{chunk-4HUXYD3B.mjs → chunk-DTRPPNSA.mjs} +2 -2
  9. package/dist/{chunk-YTXEWDNC.mjs → chunk-E6MQUIZW.mjs} +15 -4
  10. package/dist/chunk-E6MQUIZW.mjs.map +1 -0
  11. package/dist/{chunk-Z4XPPVZT.mjs → chunk-EYRKZDP5.mjs} +1 -1
  12. package/dist/chunk-EYRKZDP5.mjs.map +1 -0
  13. package/dist/{chunk-SGFUVPDB.mjs → chunk-FOSKS7AI.mjs} +4 -4
  14. package/dist/{chunk-L3GZ7TXC.mjs → chunk-GCKKSSBU.mjs} +21 -24
  15. package/dist/chunk-GCKKSSBU.mjs.map +1 -0
  16. package/dist/{chunk-S5MBUNGN.mjs → chunk-O72ZB5V3.mjs} +4 -4
  17. package/dist/chunk-O72ZB5V3.mjs.map +1 -0
  18. package/dist/{chunk-QGSPTXOV.mjs → chunk-O7KTFUAN.mjs} +2 -2
  19. package/dist/chunk-O7KTFUAN.mjs.map +1 -0
  20. package/dist/{chunk-HKTWKCPS.mjs → chunk-O7PYQO73.mjs} +127 -92
  21. package/dist/chunk-O7PYQO73.mjs.map +1 -0
  22. package/dist/{chunk-32MUWKL3.mjs → chunk-OQM7D3Z3.mjs} +22 -18
  23. package/dist/chunk-OQM7D3Z3.mjs.map +1 -0
  24. package/dist/chunk-Q2467VHZ.mjs +30 -0
  25. package/dist/chunk-Q2467VHZ.mjs.map +1 -0
  26. package/dist/{chunk-ALIBUJML.mjs → chunk-QELAC6XJ.mjs} +2 -2
  27. package/dist/chunk-QELAC6XJ.mjs.map +1 -0
  28. package/dist/{chunk-H3EM63WS.mjs → chunk-TCIZDWPC.mjs} +2 -2
  29. package/dist/chunk-TCIZDWPC.mjs.map +1 -0
  30. package/dist/chunk-VVL6JFCJ.mjs +16 -0
  31. package/dist/chunk-VVL6JFCJ.mjs.map +1 -0
  32. package/dist/chunk-WHDNKXMP.mjs +135 -0
  33. package/dist/chunk-WHDNKXMP.mjs.map +1 -0
  34. package/dist/components/chat/Button.d.ts +1 -1
  35. package/dist/components/chat/Chat.d.ts +119 -28
  36. package/dist/components/chat/Chat.js +298 -658
  37. package/dist/components/chat/Chat.js.map +1 -1
  38. package/dist/components/chat/Chat.mjs +12 -15
  39. package/dist/components/chat/CodeBlock.js.map +1 -1
  40. package/dist/components/chat/CodeBlock.mjs +1 -1
  41. package/dist/components/chat/Header.d.ts +1 -1
  42. package/dist/components/chat/Header.mjs +4 -4
  43. package/dist/components/chat/Input.d.ts +1 -1
  44. package/dist/components/chat/Input.js +3 -3
  45. package/dist/components/chat/Input.js.map +1 -1
  46. package/dist/components/chat/Input.mjs +2 -2
  47. package/dist/components/chat/Markdown.js +13 -2
  48. package/dist/components/chat/Markdown.js.map +1 -1
  49. package/dist/components/chat/Markdown.mjs +2 -2
  50. package/dist/components/chat/Messages.d.ts +3 -3
  51. package/dist/components/chat/Messages.js +40 -116
  52. package/dist/components/chat/Messages.js.map +1 -1
  53. package/dist/components/chat/Messages.mjs +1 -1
  54. package/dist/components/chat/Modal.d.ts +7 -2
  55. package/dist/components/chat/Modal.js +308 -668
  56. package/dist/components/chat/Modal.js.map +1 -1
  57. package/dist/components/chat/Modal.mjs +17 -20
  58. package/dist/components/chat/Popup.d.ts +7 -2
  59. package/dist/components/chat/Popup.js +310 -670
  60. package/dist/components/chat/Popup.js.map +1 -1
  61. package/dist/components/chat/Popup.mjs +18 -21
  62. package/dist/components/chat/Sidebar.d.ts +7 -2
  63. package/dist/components/chat/Sidebar.js +312 -672
  64. package/dist/components/chat/Sidebar.js.map +1 -1
  65. package/dist/components/chat/Sidebar.mjs +18 -21
  66. package/dist/components/chat/Suggestion.d.ts +2 -9
  67. package/dist/components/chat/Suggestion.js +6 -96
  68. package/dist/components/chat/Suggestion.js.map +1 -1
  69. package/dist/components/chat/Suggestion.mjs +3 -5
  70. package/dist/components/chat/Suggestions.d.ts +1 -1
  71. package/dist/components/chat/Suggestions.js +4 -3
  72. package/dist/components/chat/Suggestions.js.map +1 -1
  73. package/dist/components/chat/Suggestions.mjs +2 -2
  74. package/dist/components/chat/Window.d.ts +1 -1
  75. package/dist/components/chat/index.d.ts +8 -3
  76. package/dist/components/chat/index.js +316 -676
  77. package/dist/components/chat/index.js.map +1 -1
  78. package/dist/components/chat/index.mjs +22 -25
  79. package/dist/components/chat/messages/AssistantMessage.d.ts +1 -1
  80. package/dist/components/chat/messages/AssistantMessage.js +32 -24
  81. package/dist/components/chat/messages/AssistantMessage.js.map +1 -1
  82. package/dist/components/chat/messages/AssistantMessage.mjs +3 -3
  83. package/dist/components/chat/messages/ImageRenderer.d.ts +12 -0
  84. package/dist/components/chat/messages/ImageRenderer.js +58 -0
  85. package/dist/components/chat/messages/ImageRenderer.js.map +1 -0
  86. package/dist/components/chat/messages/ImageRenderer.mjs +8 -0
  87. package/dist/components/chat/messages/RenderMessage.d.ts +9 -0
  88. package/dist/components/chat/messages/{RenderTextMessage.js → RenderMessage.js} +92 -47
  89. package/dist/components/chat/messages/RenderMessage.js.map +1 -0
  90. package/dist/components/chat/messages/RenderMessage.mjs +16 -0
  91. package/dist/components/chat/messages/UserMessage.d.ts +1 -1
  92. package/dist/components/chat/messages/UserMessage.js +7 -1
  93. package/dist/components/chat/messages/UserMessage.js.map +1 -1
  94. package/dist/components/chat/messages/UserMessage.mjs +1 -1
  95. package/dist/components/chat/props.d.ts +32 -27
  96. package/dist/components/chat/props.js.map +1 -1
  97. package/dist/components/dev-console/console.mjs +3 -3
  98. package/dist/components/dev-console/index.mjs +4 -4
  99. package/dist/components/index.d.ts +8 -3
  100. package/dist/components/index.js +316 -676
  101. package/dist/components/index.js.map +1 -1
  102. package/dist/components/index.mjs +23 -26
  103. package/dist/hooks/index.js.map +1 -1
  104. package/dist/hooks/index.mjs +1 -1
  105. package/dist/hooks/use-copilot-chat-suggestions.d.ts +1 -1
  106. package/dist/hooks/use-copilot-chat-suggestions.js.map +1 -1
  107. package/dist/hooks/use-copilot-chat-suggestions.mjs +1 -1
  108. package/dist/hooks/use-push-to-talk.d.ts +1 -1
  109. package/dist/hooks/use-push-to-talk.js +3 -3
  110. package/dist/hooks/use-push-to-talk.js.map +1 -1
  111. package/dist/hooks/use-push-to-talk.mjs +1 -1
  112. package/dist/index.css +59 -3
  113. package/dist/index.css.map +1 -1
  114. package/dist/index.d.ts +8 -3
  115. package/dist/index.js +322 -682
  116. package/dist/index.js.map +1 -1
  117. package/dist/index.mjs +24 -27
  118. package/dist/types/css.d.ts +3 -0
  119. package/dist/types/css.js.map +1 -1
  120. package/package.json +4 -4
  121. package/src/components/chat/Chat.tsx +194 -106
  122. package/src/components/chat/CodeBlock.tsx +1 -1
  123. package/src/components/chat/Markdown.tsx +12 -2
  124. package/src/components/chat/Messages.tsx +43 -122
  125. package/src/components/chat/Popup.tsx +1 -1
  126. package/src/components/chat/Sidebar.tsx +1 -1
  127. package/src/components/chat/Suggestion.tsx +5 -108
  128. package/src/components/chat/Suggestions.tsx +0 -1
  129. package/src/components/chat/index.tsx +1 -1
  130. package/src/components/chat/messages/AssistantMessage.tsx +15 -23
  131. package/src/components/chat/messages/ImageRenderer.tsx +37 -0
  132. package/src/components/chat/messages/{RenderTextMessage.tsx → RenderMessage.tsx} +10 -9
  133. package/src/components/chat/messages/UserMessage.tsx +16 -5
  134. package/src/components/chat/props.ts +36 -28
  135. package/src/css/colors.css +10 -0
  136. package/src/css/markdown.css +8 -0
  137. package/src/css/messages.css +54 -5
  138. package/src/css/suggestions.css +1 -1
  139. package/src/hooks/use-copilot-chat-suggestions.tsx +1 -1
  140. package/src/hooks/use-push-to-talk.tsx +6 -5
  141. package/src/styles.css +1 -1
  142. package/src/types/css.ts +3 -0
  143. package/dist/chunk-2II3Q27P.mjs +0 -112
  144. package/dist/chunk-2II3Q27P.mjs.map +0 -1
  145. package/dist/chunk-32MUWKL3.mjs.map +0 -1
  146. package/dist/chunk-53CVDVS5.mjs +0 -127
  147. package/dist/chunk-53CVDVS5.mjs.map +0 -1
  148. package/dist/chunk-ALIBUJML.mjs.map +0 -1
  149. package/dist/chunk-B3D7U7TJ.mjs +0 -211
  150. package/dist/chunk-B3D7U7TJ.mjs.map +0 -1
  151. package/dist/chunk-C7OB63U5.mjs +0 -36
  152. package/dist/chunk-C7OB63U5.mjs.map +0 -1
  153. package/dist/chunk-H3EM63WS.mjs.map +0 -1
  154. package/dist/chunk-HKTWKCPS.mjs.map +0 -1
  155. package/dist/chunk-HWMFMBJC.mjs +0 -10
  156. package/dist/chunk-HWMFMBJC.mjs.map +0 -1
  157. package/dist/chunk-IMBPSLL4.mjs +0 -104
  158. package/dist/chunk-IMBPSLL4.mjs.map +0 -1
  159. package/dist/chunk-KENCH7RN.mjs.map +0 -1
  160. package/dist/chunk-L3GZ7TXC.mjs.map +0 -1
  161. package/dist/chunk-QGSPTXOV.mjs.map +0 -1
  162. package/dist/chunk-S5MBUNGN.mjs.map +0 -1
  163. package/dist/chunk-ULDQXCED.mjs +0 -78
  164. package/dist/chunk-ULDQXCED.mjs.map +0 -1
  165. package/dist/chunk-YTXEWDNC.mjs.map +0 -1
  166. package/dist/chunk-Z4XPPVZT.mjs.map +0 -1
  167. package/dist/components/chat/messages/RenderActionExecutionMessage.d.ts +0 -9
  168. package/dist/components/chat/messages/RenderActionExecutionMessage.js +0 -869
  169. package/dist/components/chat/messages/RenderActionExecutionMessage.js.map +0 -1
  170. package/dist/components/chat/messages/RenderActionExecutionMessage.mjs +0 -14
  171. package/dist/components/chat/messages/RenderAgentStateMessage.d.ts +0 -9
  172. package/dist/components/chat/messages/RenderAgentStateMessage.js +0 -854
  173. package/dist/components/chat/messages/RenderAgentStateMessage.js.map +0 -1
  174. package/dist/components/chat/messages/RenderAgentStateMessage.mjs +0 -14
  175. package/dist/components/chat/messages/RenderImageMessage.d.ts +0 -9
  176. package/dist/components/chat/messages/RenderImageMessage.js +0 -823
  177. package/dist/components/chat/messages/RenderImageMessage.js.map +0 -1
  178. package/dist/components/chat/messages/RenderImageMessage.mjs +0 -15
  179. package/dist/components/chat/messages/RenderImageMessage.mjs.map +0 -1
  180. package/dist/components/chat/messages/RenderResultMessage.d.ts +0 -9
  181. package/dist/components/chat/messages/RenderResultMessage.js +0 -778
  182. package/dist/components/chat/messages/RenderResultMessage.js.map +0 -1
  183. package/dist/components/chat/messages/RenderResultMessage.mjs +0 -14
  184. package/dist/components/chat/messages/RenderResultMessage.mjs.map +0 -1
  185. package/dist/components/chat/messages/RenderTextMessage.d.ts +0 -9
  186. package/dist/components/chat/messages/RenderTextMessage.js.map +0 -1
  187. package/dist/components/chat/messages/RenderTextMessage.mjs +0 -15
  188. package/dist/components/chat/messages/RenderTextMessage.mjs.map +0 -1
  189. package/src/components/chat/messages/RenderActionExecutionMessage.tsx +0 -127
  190. package/src/components/chat/messages/RenderAgentStateMessage.tsx +0 -116
  191. package/src/components/chat/messages/RenderImageMessage.tsx +0 -64
  192. package/src/components/chat/messages/RenderResultMessage.tsx +0 -26
  193. /package/dist/{chunk-GVKA7RQQ.mjs.map → chunk-7CAK2CNK.mjs.map} +0 -0
  194. /package/dist/{chunk-KN2GCKBE.mjs.map → chunk-7RNOT3GM.mjs.map} +0 -0
  195. /package/dist/{chunk-4HUXYD3B.mjs.map → chunk-DTRPPNSA.mjs.map} +0 -0
  196. /package/dist/{chunk-SGFUVPDB.mjs.map → chunk-FOSKS7AI.mjs.map} +0 -0
  197. /package/dist/components/chat/messages/{RenderActionExecutionMessage.mjs.map → ImageRenderer.mjs.map} +0 -0
  198. /package/dist/components/chat/messages/{RenderAgentStateMessage.mjs.map → RenderMessage.mjs.map} +0 -0
@@ -34,9 +34,19 @@ const defaultComponents: Components = {
34
34
 
35
35
  const match = /language-(\w+)/.exec(className || "");
36
36
 
37
- if (inline) {
37
+ // Detect inline code: if it has a language class or contains newlines, it's likely a code block
38
+ // Otherwise, treat it as inline code
39
+ const hasLanguage = match && match[1];
40
+ const content = String(children);
41
+ const hasNewlines = content.includes("\n");
42
+ const isInline = !hasLanguage && !hasNewlines;
43
+
44
+ if (isInline) {
38
45
  return (
39
- <code className={className} {...props}>
46
+ <code
47
+ className={`copilotKitMarkdownElement copilotKitInlineCode ${className || ""}`}
48
+ {...props}
49
+ >
40
50
  {children}
41
51
  </code>
42
52
  );
@@ -1,18 +1,13 @@
1
1
  import { useEffect, useMemo, useRef } from "react";
2
2
  import { MessagesProps } from "./props";
3
3
  import { useChatContext } from "./ChatContext";
4
- import { Message, ResultMessage, TextMessage, Role } from "@copilotkit/runtime-client-gql";
5
- import { useLangGraphInterruptRender } from "@copilotkit/react-core";
4
+ import { Message, Role } from "@copilotkit/shared";
5
+ import { useCopilotChat } from "@copilotkit/react-core";
6
6
 
7
7
  export const Messages = ({
8
- messages,
9
8
  inProgress,
10
9
  children,
11
- RenderTextMessage,
12
- RenderActionExecutionMessage,
13
- RenderAgentStateMessage,
14
- RenderResultMessage,
15
- RenderImageMessage,
10
+ RenderMessage,
16
11
  AssistantMessage,
17
12
  UserMessage,
18
13
  onRegenerate,
@@ -21,110 +16,33 @@ export const Messages = ({
21
16
  onThumbsDown,
22
17
  markdownTagRenderers,
23
18
  }: MessagesProps) => {
24
- const context = useChatContext();
25
- const initialMessages = useMemo(
26
- () => makeInitialMessages(context.labels.initial),
27
- [context.labels.initial],
28
- );
29
-
30
- messages = [...initialMessages, ...messages];
31
-
32
- const actionResults: Record<string, string> = {};
33
-
34
- for (let i = 0; i < messages.length; i++) {
35
- if (messages[i].isActionExecutionMessage()) {
36
- const id = messages[i].id;
37
- const resultMessage: ResultMessage | undefined = messages.find(
38
- (message) => message.isResultMessage() && message.actionExecutionId === id,
39
- ) as ResultMessage | undefined;
40
-
41
- if (resultMessage) {
42
- actionResults[id] = ResultMessage.decodeResult(resultMessage.result || "");
43
- }
44
- }
45
- }
46
-
19
+ const { labels } = useChatContext();
20
+ const { visibleMessages, interrupt } = useCopilotChat();
21
+ const initialMessages = useMemo(() => makeInitialMessages(labels.initial), [labels.initial]);
22
+ const messages = [...initialMessages, ...visibleMessages];
47
23
  const { messagesContainerRef, messagesEndRef } = useScrollToBottom(messages);
48
24
 
49
- const interrupt = useLangGraphInterruptRender();
50
-
51
25
  return (
52
26
  <div className="copilotKitMessages" ref={messagesContainerRef}>
53
27
  <div className="copilotKitMessagesContainer">
54
28
  {messages.map((message, index) => {
55
29
  const isCurrentMessage = index === messages.length - 1;
56
-
57
- if (message.isTextMessage()) {
58
- return (
59
- <RenderTextMessage
60
- key={index}
61
- message={message}
62
- inProgress={inProgress}
63
- index={index}
64
- isCurrentMessage={isCurrentMessage}
65
- AssistantMessage={AssistantMessage}
66
- UserMessage={UserMessage}
67
- onRegenerate={onRegenerate}
68
- onCopy={onCopy}
69
- onThumbsUp={onThumbsUp}
70
- onThumbsDown={onThumbsDown}
71
- markdownTagRenderers={markdownTagRenderers}
72
- />
73
- );
74
- } else if (message.isActionExecutionMessage()) {
75
- return (
76
- <RenderActionExecutionMessage
77
- key={index}
78
- message={message}
79
- inProgress={inProgress}
80
- index={index}
81
- isCurrentMessage={isCurrentMessage}
82
- actionResult={actionResults[message.id]}
83
- AssistantMessage={AssistantMessage}
84
- UserMessage={UserMessage}
85
- />
86
- );
87
- } else if (message.isAgentStateMessage()) {
88
- return (
89
- <RenderAgentStateMessage
90
- key={index}
91
- message={message}
92
- inProgress={inProgress}
93
- index={index}
94
- isCurrentMessage={isCurrentMessage}
95
- AssistantMessage={AssistantMessage}
96
- UserMessage={UserMessage}
97
- />
98
- );
99
- } else if (message.isResultMessage()) {
100
- return (
101
- <RenderResultMessage
102
- key={index}
103
- message={message}
104
- inProgress={inProgress}
105
- index={index}
106
- isCurrentMessage={isCurrentMessage}
107
- AssistantMessage={AssistantMessage}
108
- UserMessage={UserMessage}
109
- />
110
- );
111
- } else if (message.isImageMessage && message.isImageMessage()) {
112
- return (
113
- <RenderImageMessage
114
- key={index}
115
- message={message}
116
- inProgress={inProgress}
117
- index={index}
118
- isCurrentMessage={isCurrentMessage}
119
- AssistantMessage={AssistantMessage}
120
- UserMessage={UserMessage}
121
- onRegenerate={onRegenerate}
122
- onCopy={onCopy}
123
- onThumbsUp={onThumbsUp}
124
- onThumbsDown={onThumbsDown}
125
- />
126
- );
127
- }
30
+ return (
31
+ <RenderMessage
32
+ key={index}
33
+ message={message}
34
+ inProgress={inProgress}
35
+ index={index}
36
+ isCurrentMessage={isCurrentMessage}
37
+ AssistantMessage={AssistantMessage}
38
+ UserMessage={UserMessage}
39
+ onRegenerate={onRegenerate}
40
+ onCopy={onCopy}
41
+ onThumbsUp={onThumbsUp}
42
+ onThumbsDown={onThumbsDown}
43
+ markdownTagRenderers={markdownTagRenderers}
44
+ />
45
+ );
128
46
  })}
129
47
  {interrupt}
130
48
  </div>
@@ -135,26 +53,29 @@ export const Messages = ({
135
53
  );
136
54
  };
137
55
 
138
- function makeInitialMessages(initial?: string | string[]): Message[] {
139
- let initialArray: string[] = [];
140
- if (initial) {
141
- if (Array.isArray(initial)) {
142
- initialArray.push(...initial);
143
- } else {
144
- initialArray.push(initial);
145
- }
146
- }
56
+ function makeInitialMessages(initial: string | string[] | undefined): Message[] {
57
+ if (!initial) return [];
147
58
 
148
- return initialArray.map(
149
- (message) =>
150
- new TextMessage({
151
- role: Role.Assistant,
59
+ if (Array.isArray(initial)) {
60
+ return initial.map((message) => {
61
+ return {
62
+ id: message,
63
+ role: "assistant",
152
64
  content: message,
153
- }),
154
- );
65
+ };
66
+ });
67
+ }
68
+
69
+ return [
70
+ {
71
+ id: initial,
72
+ role: "system",
73
+ content: initial,
74
+ },
75
+ ];
155
76
  }
156
77
 
157
- export function useScrollToBottom(messages: any[]) {
78
+ export function useScrollToBottom(messages: Message[]) {
158
79
  const messagesEndRef = useRef<HTMLDivElement>(null);
159
80
  const messagesContainerRef = useRef<HTMLDivElement | null>(null);
160
81
  const isProgrammaticScrollRef = useRef(false);
@@ -217,7 +138,7 @@ export function useScrollToBottom(messages: any[]) {
217
138
  useEffect(() => {
218
139
  isUserScrollUpRef.current = false;
219
140
  scrollToBottom();
220
- }, [messages.filter((m) => m.isTextMessage() && m.role === Role.User).length]);
141
+ }, [messages.filter((m) => m.role === "user").length]);
221
142
 
222
143
  return { messagesEndRef, messagesContainerRef };
223
144
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * <br/>
3
- * <img src="/images/CopilotPopup.gif" width="500" />
3
+ * <img src="https://cdn.copilotkit.ai/docs/copilotkit/images/CopilotPopup.gif" width="500" />
4
4
  *
5
5
  * A chatbot popup component for the CopilotKit framework. The component allows for a high degree
6
6
  * of customization through various props and custom CSS.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * <br/>
3
- * <img src="/images/CopilotSidebar.gif" width="500" />
3
+ * <img src="https://cdn.copilotkit.ai/docs/copilotkit/images/CopilotSidebar.gif" width="500" />
4
4
  *
5
5
  * A chatbot sidebar component for the CopilotKit framework. Highly customizable through various props and custom CSS.
6
6
  *
@@ -1,13 +1,5 @@
1
- import {
2
- CopilotContextParams,
3
- extract,
4
- CopilotChatSuggestionConfiguration,
5
- CopilotMessagesContextParams,
6
- } from "@copilotkit/react-core";
1
+ import { useCopilotChat } from "@copilotkit/react-core";
7
2
  import { SmallSpinnerIcon } from "./Icons";
8
- import { CopilotChatSuggestion } from "../../types/suggestions";
9
- import { actionParametersToJsonSchema } from "@copilotkit/shared";
10
- import { CopilotRequestType } from "@copilotkit/runtime-client-gql";
11
3
 
12
4
  interface SuggestionsProps {
13
5
  title: string;
@@ -18,9 +10,12 @@ interface SuggestionsProps {
18
10
  }
19
11
 
20
12
  export function Suggestion({ title, onClick, partial, className }: SuggestionsProps) {
13
+ if (!title) return null;
14
+ const { isLoading } = useCopilotChat();
15
+
21
16
  return (
22
17
  <button
23
- disabled={partial}
18
+ disabled={partial || isLoading}
24
19
  onClick={(e) => {
25
20
  e.preventDefault();
26
21
  onClick();
@@ -32,101 +27,3 @@ export function Suggestion({ title, onClick, partial, className }: SuggestionsPr
32
27
  </button>
33
28
  );
34
29
  }
35
-
36
- export const reloadSuggestions = async (
37
- context: CopilotContextParams & CopilotMessagesContextParams,
38
- chatSuggestionConfiguration: { [key: string]: CopilotChatSuggestionConfiguration },
39
- setCurrentSuggestions: (suggestions: { title: string; message: string }[]) => void,
40
- abortControllerRef: React.MutableRefObject<AbortController | null>,
41
- ) => {
42
- const abortController = abortControllerRef.current;
43
-
44
- const tools = JSON.stringify(
45
- Object.values(context.actions).map((action) => ({
46
- name: action.name,
47
- description: action.description,
48
- jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters)),
49
- })),
50
- );
51
-
52
- const allSuggestions: CopilotChatSuggestion[] = [];
53
-
54
- for (const config of Object.values(chatSuggestionConfiguration)) {
55
- try {
56
- const numOfSuggestionsInstructions =
57
- config.minSuggestions === 0
58
- ? `Produce up to ${config.maxSuggestions} suggestions. ` +
59
- `If there are no highly relevant suggestions you can think of, provide an empty array.`
60
- : `Produce between ${config.minSuggestions} and ${config.maxSuggestions} suggestions.`;
61
-
62
- const result = await extract({
63
- context,
64
- instructions:
65
- "Suggest what the user could say next. Provide clear, highly relevant suggestions. Do not literally suggest function calls. ",
66
- data:
67
- config.instructions +
68
- "\n\n" +
69
- numOfSuggestionsInstructions +
70
- "\n\n" +
71
- "Available tools: " +
72
- tools +
73
- "\n\n",
74
- requestType: CopilotRequestType.Task,
75
- parameters: [
76
- {
77
- name: "suggestions",
78
- type: "object[]",
79
- attributes: [
80
- {
81
- name: "title",
82
- description:
83
- "The title of the suggestion. This is shown as a button and should be short.",
84
- type: "string",
85
- },
86
- {
87
- name: "message",
88
- description:
89
- "The message to send when the suggestion is clicked. This should be a clear, complete sentence and will be sent as an instruction to the AI.",
90
- type: "string",
91
- },
92
- ],
93
- },
94
- ],
95
- include: {
96
- messages: true,
97
- readable: true,
98
- },
99
- abortSignal: abortController?.signal,
100
- stream: ({ status, args }) => {
101
- const suggestions = args.suggestions || [];
102
- const newSuggestions: CopilotChatSuggestion[] = [];
103
- for (let i = 0; i < suggestions.length; i++) {
104
- // if GPT provides too many suggestions, limit the number of suggestions
105
- if (config.maxSuggestions !== undefined && i >= config.maxSuggestions) {
106
- break;
107
- }
108
- const { title, message } = suggestions[i];
109
-
110
- // If this is the last suggestion and the status is not complete, mark it as partial
111
- const partial = i == suggestions.length - 1 && status !== "complete";
112
-
113
- newSuggestions.push({
114
- title,
115
- message,
116
- partial,
117
- className: config.className,
118
- });
119
- }
120
- setCurrentSuggestions([...allSuggestions, ...newSuggestions]);
121
- },
122
- });
123
- allSuggestions.push(...result.suggestions);
124
- } catch (error) {
125
- console.error("Error loading suggestions", error);
126
- }
127
- }
128
-
129
- if (abortControllerRef.current === abortController) {
130
- abortControllerRef.current = null;
131
- }
132
- };
@@ -1,5 +1,4 @@
1
1
  import { Suggestion } from "./Suggestion";
2
- import React from "react";
3
2
  import { RenderSuggestionsListProps } from "./props";
4
3
 
5
4
  export function Suggestions({ suggestions, onSuggestionClick }: RenderSuggestionsListProps) {
@@ -5,7 +5,7 @@ export { CopilotChat } from "./Chat";
5
5
  export { Markdown } from "./Markdown";
6
6
  export { AssistantMessage } from "./messages/AssistantMessage";
7
7
  export { UserMessage } from "./messages/UserMessage";
8
+ export { ImageRenderer } from "./messages/ImageRenderer";
8
9
  export { useChatContext } from "./ChatContext";
9
- export { RenderImageMessage } from "./messages/RenderImageMessage";
10
10
  export { Suggestions as RenderSuggestionsList } from "./Suggestions";
11
11
  export { Suggestion as RenderSuggestion } from "./Suggestion";
@@ -2,67 +2,58 @@ import { AssistantMessageProps } from "../props";
2
2
  import { useChatContext } from "../ChatContext";
3
3
  import { Markdown } from "../Markdown";
4
4
  import { useState } from "react";
5
- import { TextMessage } from "@copilotkit/runtime-client-gql";
6
5
 
7
6
  export const AssistantMessage = (props: AssistantMessageProps) => {
8
7
  const { icons, labels } = useChatContext();
9
8
  const {
10
9
  message,
11
10
  isLoading,
12
- subComponent,
13
11
  onRegenerate,
14
12
  onCopy,
15
13
  onThumbsUp,
16
14
  onThumbsDown,
17
15
  isCurrentMessage,
18
- rawData,
19
16
  markdownTagRenderers,
20
17
  } = props;
21
18
  const [copied, setCopied] = useState(false);
22
19
 
23
20
  const handleCopy = () => {
24
- if (message && onCopy) {
25
- navigator.clipboard.writeText(message);
21
+ const content = message?.content || "";
22
+ if (content && onCopy) {
23
+ navigator.clipboard.writeText(content);
26
24
  setCopied(true);
27
- onCopy(message);
25
+ onCopy(content);
28
26
  setTimeout(() => setCopied(false), 2000);
29
- } else if (message) {
30
- navigator.clipboard.writeText(message);
27
+ } else if (content) {
28
+ navigator.clipboard.writeText(content);
31
29
  setCopied(true);
32
30
  setTimeout(() => setCopied(false), 2000);
33
31
  }
34
32
  };
35
33
 
36
34
  const handleRegenerate = () => {
37
- if (onRegenerate) {
38
- onRegenerate();
39
- }
35
+ if (onRegenerate) onRegenerate();
40
36
  };
41
37
 
42
38
  const handleThumbsUp = () => {
43
- const fullMessage = rawData as TextMessage;
44
- if (onThumbsUp && fullMessage) {
45
- onThumbsUp(fullMessage);
46
- }
39
+ if (onThumbsUp && message) onThumbsUp(message);
47
40
  };
48
41
 
49
42
  const handleThumbsDown = () => {
50
- const fullMessage = rawData as TextMessage;
51
- if (onThumbsDown && fullMessage) {
52
- onThumbsDown(fullMessage);
53
- }
43
+ if (onThumbsDown && message) onThumbsDown(message);
54
44
  };
55
45
 
56
46
  const LoadingIcon = () => <span>{icons.activityIcon}</span>;
47
+ const content = message?.content || "";
48
+ const subComponent = message?.generativeUI?.();
57
49
 
58
50
  return (
59
51
  <>
60
- {(message || isLoading) && (
52
+ {content && (
61
53
  <div className="copilotKitMessage copilotKitAssistantMessage">
62
- {message && <Markdown content={message || ""} components={markdownTagRenderers} />}
63
- {isLoading && <LoadingIcon />}
54
+ {content && <Markdown content={content} components={markdownTagRenderers} />}
64
55
 
65
- {message && !isLoading && (
56
+ {content && !isLoading && (
66
57
  <div
67
58
  className={`copilotKitMessageControls ${isCurrentMessage ? "currentMessage" : ""}`}
68
59
  >
@@ -111,6 +102,7 @@ export const AssistantMessage = (props: AssistantMessageProps) => {
111
102
  </div>
112
103
  )}
113
104
  <div style={{ marginBottom: "0.5rem" }}>{subComponent}</div>
105
+ {isLoading && <LoadingIcon />}
114
106
  </>
115
107
  );
116
108
  };
@@ -0,0 +1,37 @@
1
+ import React, { useState } from "react";
2
+ import { ImageRendererProps } from "../props";
3
+
4
+ /**
5
+ * Default image rendering component that can be customized by users.
6
+ * Uses CSS classes for styling so users can override styles.
7
+ */
8
+ export const ImageRenderer: React.FC<ImageRendererProps> = ({ image, content, className = "" }) => {
9
+ const [imageError, setImageError] = useState(false);
10
+ const imageSrc = `data:image/${image.format};base64,${image.bytes}`;
11
+ const altText = content || "User uploaded image";
12
+
13
+ const handleImageError = () => {
14
+ setImageError(true);
15
+ };
16
+
17
+ if (imageError) {
18
+ return (
19
+ <div className={`copilotKitImageRendering copilotKitImageRenderingError ${className}`}>
20
+ <div className="copilotKitImageRenderingErrorMessage">Failed to load image</div>
21
+ {content && <div className="copilotKitImageRenderingContent">{content}</div>}
22
+ </div>
23
+ );
24
+ }
25
+
26
+ return (
27
+ <div className={`copilotKitImageRendering ${className}`}>
28
+ <img
29
+ src={imageSrc}
30
+ alt={altText}
31
+ className="copilotKitImageRenderingImage"
32
+ onError={handleImageError}
33
+ />
34
+ {content && <div className="copilotKitImageRenderingContent">{content}</div>}
35
+ </div>
36
+ );
37
+ };
@@ -1,10 +1,12 @@
1
1
  import { RenderMessageProps } from "../props";
2
2
  import { UserMessage as DefaultUserMessage } from "./UserMessage";
3
3
  import { AssistantMessage as DefaultAssistantMessage } from "./AssistantMessage";
4
+ import { ImageRenderer as DefaultImageRenderer } from "./ImageRenderer";
4
5
 
5
- export function RenderTextMessage({
6
+ export function RenderMessage({
6
7
  UserMessage = DefaultUserMessage,
7
8
  AssistantMessage = DefaultAssistantMessage,
9
+ ImageRenderer = DefaultImageRenderer,
8
10
  ...props
9
11
  }: RenderMessageProps) {
10
12
  const {
@@ -19,23 +21,22 @@ export function RenderTextMessage({
19
21
  markdownTagRenderers,
20
22
  } = props;
21
23
 
22
- if (message.isTextMessage()) {
23
- if (message.role === "user") {
24
+ switch (message.role) {
25
+ case "user":
24
26
  return (
25
27
  <UserMessage
26
28
  key={index}
27
29
  data-message-role="user"
28
- message={message.content}
29
- rawData={message}
30
+ message={message}
31
+ ImageRenderer={ImageRenderer}
30
32
  />
31
33
  );
32
- } else if (message.role == "assistant") {
34
+ case "assistant":
33
35
  return (
34
36
  <AssistantMessage
35
37
  key={index}
36
38
  data-message-role="assistant"
37
- message={message.content}
38
- rawData={message}
39
+ message={message}
39
40
  isLoading={inProgress && isCurrentMessage && !message.content}
40
41
  isGenerating={inProgress && isCurrentMessage && !!message.content}
41
42
  isCurrentMessage={isCurrentMessage}
@@ -44,8 +45,8 @@ export function RenderTextMessage({
44
45
  onThumbsUp={onThumbsUp}
45
46
  onThumbsDown={onThumbsDown}
46
47
  markdownTagRenderers={markdownTagRenderers}
48
+ ImageRenderer={ImageRenderer}
47
49
  />
48
50
  );
49
- }
50
51
  }
51
52
  }
@@ -1,9 +1,20 @@
1
1
  import { UserMessageProps } from "../props";
2
2
 
3
3
  export const UserMessage = (props: UserMessageProps) => {
4
- return (
5
- <div className="copilotKitMessage copilotKitUserMessage">
6
- {props.subComponent || props.message}
7
- </div>
8
- );
4
+ const { message, ImageRenderer } = props;
5
+ const isImageMessage = message && "image" in message && message.image;
6
+
7
+ // Image message
8
+ if (isImageMessage) {
9
+ const imageMessage = message;
10
+
11
+ return (
12
+ <div className="copilotKitMessage copilotKitUserMessage">
13
+ <ImageRenderer image={imageMessage.image!} content={imageMessage.content} />
14
+ </div>
15
+ );
16
+ }
17
+
18
+ // Regular text message
19
+ return <div className="copilotKitMessage copilotKitUserMessage">{message?.content}</div>;
9
20
  };