@copilotkit/react-ui 1.7.2-next.1 → 1.8.0-next.3

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 (201) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/{chunk-QXTRFMPM.mjs → chunk-2LRE4W6A.mjs} +13 -11
  3. package/dist/chunk-2LRE4W6A.mjs.map +1 -0
  4. package/dist/{chunk-2C3ANQCY.mjs → chunk-5GNYGURH.mjs} +53 -42
  5. package/dist/chunk-5GNYGURH.mjs.map +1 -0
  6. package/dist/{chunk-CBBFRI3Q.mjs → chunk-5JY5QJ2W.mjs} +9 -5
  7. package/dist/chunk-5JY5QJ2W.mjs.map +1 -0
  8. package/dist/{chunk-OFYI4UU4.mjs → chunk-7RIBDD4K.mjs} +19 -3
  9. package/dist/chunk-7RIBDD4K.mjs.map +1 -0
  10. package/dist/{chunk-YAGE7RCE.mjs → chunk-CGVOCLHN.mjs} +2 -2
  11. package/dist/chunk-EJG6RRSX.mjs +138 -0
  12. package/dist/chunk-EJG6RRSX.mjs.map +1 -0
  13. package/dist/chunk-FBYETUFL.mjs +118 -0
  14. package/dist/chunk-FBYETUFL.mjs.map +1 -0
  15. package/dist/chunk-GDJAAFIK.mjs +24 -0
  16. package/dist/chunk-GDJAAFIK.mjs.map +1 -0
  17. package/dist/{chunk-6FTRYYR5.mjs → chunk-GJME6MK4.mjs} +72 -62
  18. package/dist/chunk-GJME6MK4.mjs.map +1 -0
  19. package/dist/{chunk-AZU4QOV5.mjs → chunk-KG6DW6R2.mjs} +12 -12
  20. package/dist/{chunk-3PJA5MFR.mjs → chunk-LKCAF2HG.mjs} +2 -2
  21. package/dist/{chunk-ADTTDBLB.mjs → chunk-LXCH2BIB.mjs} +2 -2
  22. package/dist/chunk-MCO235PS.mjs +164 -0
  23. package/dist/chunk-MCO235PS.mjs.map +1 -0
  24. package/dist/chunk-MWC5OV7Z.mjs +1 -0
  25. package/dist/chunk-N7LTE54T.mjs +1 -0
  26. package/dist/chunk-N7LTE54T.mjs.map +1 -0
  27. package/dist/chunk-ORSMX3SE.mjs +244 -0
  28. package/dist/chunk-ORSMX3SE.mjs.map +1 -0
  29. package/dist/{chunk-UPTB2MVO.mjs → chunk-PCTCOQK2.mjs} +4 -10
  30. package/dist/chunk-PCTCOQK2.mjs.map +1 -0
  31. package/dist/{chunk-22K5DDPF.mjs → chunk-QGK5GOSC.mjs} +31 -60
  32. package/dist/chunk-QGK5GOSC.mjs.map +1 -0
  33. package/dist/{chunk-WGAZXTUA.mjs → chunk-TOQ7P4DO.mjs} +6 -9
  34. package/dist/chunk-TOQ7P4DO.mjs.map +1 -0
  35. package/dist/chunk-UCVCAGU7.mjs +1 -0
  36. package/dist/chunk-UCVCAGU7.mjs.map +1 -0
  37. package/dist/{chunk-3XAXY2Z3.mjs → chunk-UZTZXMYS.mjs} +2 -2
  38. package/dist/{chunk-RQNJNK2W.mjs → chunk-VC4NO5QZ.mjs} +2 -2
  39. package/dist/{chunk-YQ3D5IQV.mjs → chunk-XNQO5AZZ.mjs} +2 -5
  40. package/dist/chunk-XNQO5AZZ.mjs.map +1 -0
  41. package/dist/chunk-YC4NBUGE.mjs +97 -0
  42. package/dist/chunk-YC4NBUGE.mjs.map +1 -0
  43. package/dist/components/chat/Button.js.map +1 -1
  44. package/dist/components/chat/Button.mjs +3 -3
  45. package/dist/components/chat/Chat.d.ts +17 -1
  46. package/dist/components/chat/Chat.js +378 -905
  47. package/dist/components/chat/Chat.js.map +1 -1
  48. package/dist/components/chat/Chat.mjs +12 -19
  49. package/dist/components/chat/ChatContext.d.ts +20 -0
  50. package/dist/components/chat/ChatContext.js +44 -74
  51. package/dist/components/chat/ChatContext.js.map +1 -1
  52. package/dist/components/chat/ChatContext.mjs +2 -2
  53. package/dist/components/chat/CodeBlock.js +58 -82
  54. package/dist/components/chat/CodeBlock.js.map +1 -1
  55. package/dist/components/chat/CodeBlock.mjs +2 -2
  56. package/dist/components/chat/Header.js +516 -4
  57. package/dist/components/chat/Header.js.map +1 -1
  58. package/dist/components/chat/Header.mjs +10 -3
  59. package/dist/components/chat/Icons.d.ts +10 -9
  60. package/dist/components/chat/Icons.js +125 -164
  61. package/dist/components/chat/Icons.js.map +1 -1
  62. package/dist/components/chat/Icons.mjs +9 -5
  63. package/dist/components/chat/Input.d.ts +1 -1
  64. package/dist/components/chat/Input.js +11 -9
  65. package/dist/components/chat/Input.js.map +1 -1
  66. package/dist/components/chat/Input.mjs +3 -3
  67. package/dist/components/chat/Markdown.js +58 -56
  68. package/dist/components/chat/Markdown.js.map +1 -1
  69. package/dist/components/chat/Markdown.mjs +3 -3
  70. package/dist/components/chat/Messages.d.ts +1 -1
  71. package/dist/components/chat/Messages.js +70 -60
  72. package/dist/components/chat/Messages.js.map +1 -1
  73. package/dist/components/chat/Messages.mjs +3 -3
  74. package/dist/components/chat/Modal.js +1708 -1749
  75. package/dist/components/chat/Modal.js.map +1 -1
  76. package/dist/components/chat/Modal.mjs +23 -23
  77. package/dist/components/chat/Popup.js +1708 -1749
  78. package/dist/components/chat/Popup.js.map +1 -1
  79. package/dist/components/chat/Popup.mjs +24 -24
  80. package/dist/components/chat/Response.js.map +1 -1
  81. package/dist/components/chat/Response.mjs +3 -3
  82. package/dist/components/chat/Sidebar.js +1710 -1751
  83. package/dist/components/chat/Sidebar.js.map +1 -1
  84. package/dist/components/chat/Sidebar.mjs +24 -24
  85. package/dist/components/chat/Suggestion.js +4 -40
  86. package/dist/components/chat/Suggestion.js.map +1 -1
  87. package/dist/components/chat/Suggestion.mjs +2 -2
  88. package/dist/components/chat/Window.js.map +1 -1
  89. package/dist/components/chat/Window.mjs +3 -3
  90. package/dist/components/chat/index.js +1710 -1751
  91. package/dist/components/chat/index.js.map +1 -1
  92. package/dist/components/chat/index.mjs +27 -27
  93. package/dist/components/chat/messages/AssistantMessage.js +211 -59
  94. package/dist/components/chat/messages/AssistantMessage.js.map +1 -1
  95. package/dist/components/chat/messages/AssistantMessage.mjs +5 -5
  96. package/dist/components/chat/messages/RenderTextMessage.js +18 -2
  97. package/dist/components/chat/messages/RenderTextMessage.js.map +1 -1
  98. package/dist/components/chat/messages/RenderTextMessage.mjs +1 -1
  99. package/dist/components/chat/props.d.ts +53 -0
  100. package/dist/components/chat/props.js.map +1 -1
  101. package/dist/components/crew/DefaultResponseRenderer.d.ts +110 -0
  102. package/dist/components/crew/DefaultResponseRenderer.js +175 -0
  103. package/dist/components/crew/DefaultResponseRenderer.js.map +1 -0
  104. package/dist/components/crew/DefaultResponseRenderer.mjs +10 -0
  105. package/dist/components/crew/DefaultResponseRenderer.mjs.map +1 -0
  106. package/dist/components/crew/DefaultStateRenderer.d.ts +88 -0
  107. package/dist/components/crew/DefaultStateRenderer.js +198 -0
  108. package/dist/components/crew/DefaultStateRenderer.js.map +1 -0
  109. package/dist/components/crew/DefaultStateRenderer.mjs +8 -0
  110. package/dist/components/crew/DefaultStateRenderer.mjs.map +1 -0
  111. package/dist/components/crew/index.d.ts +4 -0
  112. package/dist/components/crew/index.js +335 -0
  113. package/dist/components/crew/index.js.map +1 -0
  114. package/dist/components/crew/index.mjs +16 -0
  115. package/dist/components/crew/index.mjs.map +1 -0
  116. package/dist/components/crew/types.d.ts +340 -0
  117. package/dist/components/crew/types.js +19 -0
  118. package/dist/components/crew/types.js.map +1 -0
  119. package/dist/components/crew/types.mjs +2 -0
  120. package/dist/components/crew/types.mjs.map +1 -0
  121. package/dist/components/dev-console/console.js +51 -233
  122. package/dist/components/dev-console/console.js.map +1 -1
  123. package/dist/components/dev-console/console.mjs +5 -5
  124. package/dist/components/dev-console/index.js +51 -233
  125. package/dist/components/dev-console/index.js.map +1 -1
  126. package/dist/components/dev-console/index.mjs +5 -5
  127. package/dist/components/help-modal/index.js +29 -147
  128. package/dist/components/help-modal/index.js.map +1 -1
  129. package/dist/components/help-modal/index.mjs +1 -1
  130. package/dist/components/help-modal/modal.js +29 -147
  131. package/dist/components/help-modal/modal.js.map +1 -1
  132. package/dist/components/help-modal/modal.mjs +1 -1
  133. package/dist/components/index.d.ts +3 -0
  134. package/dist/components/index.js +2191 -1942
  135. package/dist/components/index.js.map +1 -1
  136. package/dist/components/index.mjs +43 -31
  137. package/dist/index.css +481 -90
  138. package/dist/index.css.map +1 -1
  139. package/dist/index.d.ts +3 -0
  140. package/dist/index.js +2192 -1943
  141. package/dist/index.js.map +1 -1
  142. package/dist/index.mjs +43 -31
  143. package/dist/types/css.d.ts +7 -1
  144. package/dist/types/css.js.map +1 -1
  145. package/package.json +4 -4
  146. package/src/components/chat/Chat.tsx +59 -22
  147. package/src/components/chat/ChatContext.tsx +29 -1
  148. package/src/components/chat/CodeBlock.tsx +2 -4
  149. package/src/components/chat/Header.tsx +8 -3
  150. package/src/components/chat/Icons.tsx +108 -108
  151. package/src/components/chat/Input.tsx +42 -38
  152. package/src/components/chat/Markdown.tsx +0 -3
  153. package/src/components/chat/Messages.tsx +68 -56
  154. package/src/components/chat/Suggestion.tsx +2 -3
  155. package/src/components/chat/messages/AssistantMessage.tsx +95 -3
  156. package/src/components/chat/messages/RenderTextMessage.tsx +17 -1
  157. package/src/components/chat/props.ts +66 -0
  158. package/src/components/crew/DefaultResponseRenderer.tsx +298 -0
  159. package/src/components/crew/DefaultStateRenderer.tsx +326 -0
  160. package/src/components/crew/index.ts +3 -0
  161. package/src/components/crew/types.ts +398 -0
  162. package/src/components/dev-console/console.tsx +16 -54
  163. package/src/components/help-modal/modal.tsx +38 -101
  164. package/src/components/index.ts +1 -0
  165. package/src/css/button.css +15 -4
  166. package/src/css/colors.css +27 -6
  167. package/src/css/console.css +46 -39
  168. package/src/css/crew.css +277 -0
  169. package/src/css/header.css +22 -5
  170. package/src/css/input.css +24 -17
  171. package/src/css/markdown.css +2 -1
  172. package/src/css/messages.css +125 -15
  173. package/src/css/panel.css +1 -0
  174. package/src/css/suggestions.css +14 -6
  175. package/src/styles.css +1 -0
  176. package/src/types/css.ts +7 -1
  177. package/dist/chunk-22K5DDPF.mjs.map +0 -1
  178. package/dist/chunk-2C3ANQCY.mjs.map +0 -1
  179. package/dist/chunk-3VNMQWGT.mjs +0 -25
  180. package/dist/chunk-3VNMQWGT.mjs.map +0 -1
  181. package/dist/chunk-6FTRYYR5.mjs.map +0 -1
  182. package/dist/chunk-CBBFRI3Q.mjs.map +0 -1
  183. package/dist/chunk-FZC7X5PK.mjs +0 -262
  184. package/dist/chunk-FZC7X5PK.mjs.map +0 -1
  185. package/dist/chunk-MMVDU6DF.mjs +0 -1
  186. package/dist/chunk-OFYI4UU4.mjs.map +0 -1
  187. package/dist/chunk-QXTRFMPM.mjs.map +0 -1
  188. package/dist/chunk-TI7SY2RI.mjs +0 -164
  189. package/dist/chunk-TI7SY2RI.mjs.map +0 -1
  190. package/dist/chunk-UPTB2MVO.mjs.map +0 -1
  191. package/dist/chunk-VEC45H6Q.mjs +0 -18
  192. package/dist/chunk-VEC45H6Q.mjs.map +0 -1
  193. package/dist/chunk-WGAZXTUA.mjs.map +0 -1
  194. package/dist/chunk-YQ3D5IQV.mjs.map +0 -1
  195. /package/dist/{chunk-YAGE7RCE.mjs.map → chunk-CGVOCLHN.mjs.map} +0 -0
  196. /package/dist/{chunk-AZU4QOV5.mjs.map → chunk-KG6DW6R2.mjs.map} +0 -0
  197. /package/dist/{chunk-3PJA5MFR.mjs.map → chunk-LKCAF2HG.mjs.map} +0 -0
  198. /package/dist/{chunk-ADTTDBLB.mjs.map → chunk-LXCH2BIB.mjs.map} +0 -0
  199. /package/dist/{chunk-MMVDU6DF.mjs.map → chunk-MWC5OV7Z.mjs.map} +0 -0
  200. /package/dist/{chunk-3XAXY2Z3.mjs.map → chunk-UZTZXMYS.mjs.map} +0 -0
  201. /package/dist/{chunk-RQNJNK2W.mjs.map → chunk-VC4NO5QZ.mjs.map} +0 -0
@@ -0,0 +1,298 @@
1
+ /**
2
+ * <br/>
3
+ * A response renderer component for the CopilotKit framework. This component displays
4
+ * a response that may require user feedback, such as approving or rejecting a suggestion.
5
+ * It provides a flexible, customizable interface for rendering responses with user interaction.
6
+ *
7
+ * ## Install Dependencies
8
+ *
9
+ * This component is part of the [@copilotkit/react-ui](https://npmjs.com/package/@copilotkit/react-ui) package.
10
+ *
11
+ * ```shell npm2yarn \"@copilotkit/react-ui"\
12
+ * npm install @copilotkit/react-core @copilotkit/react-ui
13
+ * ```
14
+ *
15
+ * ## Usage
16
+ *
17
+ * ```tsx
18
+ * import { DefaultResponseRenderer } from "@copilotkit/react-ui";
19
+ * import "@copilotkit/react-ui/styles.css";
20
+ *
21
+ * // Basic usage
22
+ * <DefaultResponseRenderer
23
+ * response={{
24
+ * id: "response-1",
25
+ * content: "I've analyzed your data and found these insights..."
26
+ * }}
27
+ * status="inProgress"
28
+ * onRespond={(input) => console.log(`User responded: ${input}`)}
29
+ * />
30
+ * ```
31
+ *
32
+ * ## Customization
33
+ *
34
+ * You can customize the appearance and behavior of the component:
35
+ *
36
+ * ```tsx
37
+ * // Custom labels and styling
38
+ * <DefaultResponseRenderer
39
+ * response={{
40
+ * id: "task-123",
41
+ * content: "Would you like to proceed with this recommendation?"
42
+ * }}
43
+ * status="inProgress"
44
+ * onRespond={handleResponse}
45
+ * labels={{
46
+ * responseLabel: "AI Recommendation",
47
+ * approveLabel: "Yes, proceed",
48
+ * rejectLabel: "No, cancel",
49
+ * approvedMessage: "Proceeding with recommendation",
50
+ * rejectedMessage: "Recommendation cancelled"
51
+ * }}
52
+ * className="my-custom-response"
53
+ * contentClassName="my-custom-content"
54
+ * buttonClassName="my-custom-button"
55
+ * />
56
+ *
57
+ * // Custom components
58
+ * <DefaultResponseRenderer
59
+ * response={{
60
+ * id: "task-456",
61
+ * content: "# Important Decision\nThis requires your approval"
62
+ * }}
63
+ * status="inProgress"
64
+ * onRespond={handleResponse}
65
+ * ContentRenderer={({ content, className }) => (
66
+ * <MyMarkdownRenderer content={content} className={className} />
67
+ * )}
68
+ * FeedbackButton={({ label, onClick, className }) => (
69
+ * <MyCustomButton label={label} onClick={onClick} className={className} />
70
+ * )}
71
+ * />
72
+ * ```
73
+ *
74
+ * ### Look & Feel
75
+ *
76
+ * By default, CopilotKit components do not have any styles. You can import CopilotKit's stylesheet at the root of your project:
77
+ * ```tsx title="YourRootComponent.tsx"
78
+ * ...
79
+ * import "@copilotkit/react-ui/styles.css"; // [!code highlight]
80
+ *
81
+ * export function YourRootComponent() {
82
+ * return (
83
+ * <CopilotKit>
84
+ * ...
85
+ * </CopilotKit>
86
+ * );
87
+ * }
88
+ * ```
89
+ * For more information about how to customize the styles, check out the [Customize Look & Feel](/guides/custom-look-and-feel/customize-built-in-ui-components) guide.
90
+ */
91
+
92
+ import React, { useState } from "react";
93
+ import {
94
+ Response,
95
+ ResponseRendererProps,
96
+ ContentRendererProps,
97
+ FeedbackButtonProps,
98
+ CompletedFeedbackProps,
99
+ ResponseRendererIconProps,
100
+ } from "./types";
101
+
102
+ /**
103
+ * Creates a cache for storing response feedback
104
+ */
105
+ const createResponseCache = <T extends { id: string }>() => {
106
+ const responseCache = new Map<string, T>();
107
+
108
+ return {
109
+ getResponse: (id: string) => responseCache.get(id),
110
+ setResponse: (id: string, response: T) => responseCache.set(id, response),
111
+ };
112
+ };
113
+
114
+ /**
115
+ * Default global response cache instance
116
+ */
117
+ const useResponseCache = createResponseCache<Response & { __feedback__?: string }>();
118
+
119
+ /**
120
+ * Default expand icon component
121
+ */
122
+ const DefaultExpandIcon: React.FC<ResponseRendererIconProps> = ({ className }) => (
123
+ <svg
124
+ className={className}
125
+ width="12"
126
+ height="12"
127
+ viewBox="0 0 24 24"
128
+ fill="none"
129
+ stroke="currentColor"
130
+ strokeWidth="2"
131
+ strokeLinecap="round"
132
+ strokeLinejoin="round"
133
+ >
134
+ <polyline points="6 9 12 15 18 9"></polyline>
135
+ </svg>
136
+ );
137
+
138
+ /**
139
+ * Default collapse icon component
140
+ */
141
+ const DefaultCollapseIcon: React.FC<ResponseRendererIconProps> = ({ className }) => (
142
+ <svg
143
+ className={className}
144
+ width="12"
145
+ height="12"
146
+ viewBox="0 0 24 24"
147
+ fill="none"
148
+ stroke="currentColor"
149
+ strokeWidth="2"
150
+ strokeLinecap="round"
151
+ strokeLinejoin="round"
152
+ >
153
+ <polyline points="18 15 12 9 6 15"></polyline>
154
+ </svg>
155
+ );
156
+
157
+ /**
158
+ * Default content renderer that simply displays text
159
+ */
160
+ const DefaultContentRenderer: React.FC<ContentRendererProps> = ({ content, className }) => (
161
+ <div className={className}>{content}</div>
162
+ );
163
+
164
+ /**
165
+ * Default feedback button component
166
+ */
167
+ const DefaultFeedbackButton: React.FC<FeedbackButtonProps> = ({ label, onClick, className }) => (
168
+ <button onClick={onClick} className={className}>
169
+ {label}
170
+ </button>
171
+ );
172
+
173
+ /**
174
+ * Default completed feedback component
175
+ */
176
+ const DefaultCompletedFeedback: React.FC<CompletedFeedbackProps> = ({ message, className }) => (
177
+ <div className={className}>
178
+ <span>{message}</span>
179
+ </div>
180
+ );
181
+
182
+ /**
183
+ * Default response renderer component that handles rendering responses
184
+ * and collecting user feedback
185
+ */
186
+ export const DefaultResponseRenderer: React.FC<ResponseRendererProps> = ({
187
+ response,
188
+ status,
189
+ onRespond,
190
+ icons,
191
+ labels,
192
+ ContentRenderer = DefaultContentRenderer,
193
+ FeedbackButton = DefaultFeedbackButton,
194
+ CompletedFeedback = DefaultCompletedFeedback,
195
+ className = "copilotkit-response",
196
+ contentClassName = "copilotkit-response-content",
197
+ actionsClassName = "copilotkit-response-actions",
198
+ buttonClassName = "copilotkit-response-button",
199
+ completedFeedbackClassName = "copilotkit-response-completed-feedback",
200
+ }) => {
201
+ const [isExpanded, setIsExpanded] = useState(true);
202
+
203
+ // Default label values
204
+ const defaultLabels = {
205
+ responseLabel: "Response",
206
+ approveLabel: "Approve",
207
+ rejectLabel: "Reject",
208
+ approvedMessage: "Approved",
209
+ rejectedMessage: "Rejected",
210
+ feedbackSubmittedMessage: "Feedback submitted",
211
+ };
212
+
213
+ // Merge provided labels with defaults
214
+ const mergedLabels = { ...defaultLabels, ...labels };
215
+
216
+ // Function to render feedback UI based on status
217
+ const renderFeedback = () => {
218
+ if (status === "complete") {
219
+ const cachedResponse = useResponseCache.getResponse(response.id);
220
+ return (
221
+ <CompletedFeedback
222
+ message={
223
+ cachedResponse?.__feedback__
224
+ ? cachedResponse.__feedback__ === mergedLabels.approvedMessage
225
+ ? mergedLabels.approvedMessage
226
+ : mergedLabels.rejectedMessage
227
+ : mergedLabels.feedbackSubmittedMessage
228
+ }
229
+ className={completedFeedbackClassName}
230
+ />
231
+ );
232
+ }
233
+
234
+ if (status === "inProgress" || status === "executing") {
235
+ return (
236
+ <>
237
+ <FeedbackButton
238
+ label={mergedLabels.approveLabel}
239
+ onClick={() => {
240
+ setIsExpanded(false);
241
+ onRespond?.(mergedLabels.approveLabel);
242
+ useResponseCache.setResponse(response.id, {
243
+ ...response,
244
+ __feedback__: mergedLabels.approvedMessage,
245
+ });
246
+ }}
247
+ className={buttonClassName}
248
+ />
249
+ <FeedbackButton
250
+ label={mergedLabels.rejectLabel}
251
+ onClick={() => {
252
+ setIsExpanded(false);
253
+ useResponseCache.setResponse(response.id, {
254
+ ...response,
255
+ __feedback__: mergedLabels.rejectedMessage,
256
+ });
257
+ onRespond?.(mergedLabels.rejectLabel);
258
+ }}
259
+ className={buttonClassName}
260
+ />
261
+ </>
262
+ );
263
+ }
264
+
265
+ return null;
266
+ };
267
+
268
+ // Decide which icon to display
269
+ const ExpandIcon = icons?.expand || DefaultExpandIcon;
270
+ const CollapseIcon = icons?.collapse || DefaultCollapseIcon;
271
+
272
+ return (
273
+ <div className={className}>
274
+ {/* Response content - conditionally expanded */}
275
+ {isExpanded && <ContentRenderer content={response.content} className={contentClassName} />}
276
+
277
+ <div className={actionsClassName}>
278
+ <div className="copilotkit-response-label">
279
+ <button onClick={() => setIsExpanded(!isExpanded)} className="copilotkit-toggle-button">
280
+ {isExpanded ? (
281
+ <CollapseIcon className="copilotkit-icon" />
282
+ ) : (
283
+ <ExpandIcon className="copilotkit-icon" />
284
+ )}
285
+ </button>
286
+ <span>{mergedLabels.responseLabel}</span>
287
+ </div>
288
+
289
+ <div className="copilotkit-response-buttons">{renderFeedback()}</div>
290
+ </div>
291
+ </div>
292
+ );
293
+ };
294
+
295
+ /**
296
+ * Export the response cache for reuse
297
+ */
298
+ export { createResponseCache };
@@ -0,0 +1,326 @@
1
+ /**
2
+ * <br/>
3
+ * A state renderer component for the CopilotKit framework. This component displays
4
+ * the internal state of an agent, such as the steps it has taken and the tasks it has performed.
5
+ * It provides a collapsible UI that shows the agent's thought process and actions.
6
+ *
7
+ * ## Install Dependencies
8
+ *
9
+ * This component is part of the [@copilotkit/react-ui](https://npmjs.com/package/@copilotkit/react-ui) package.
10
+ *
11
+ * ```shell npm2yarn \"@copilotkit/react-ui"\
12
+ * npm install @copilotkit/react-core @copilotkit/react-ui
13
+ * ```
14
+ *
15
+ * ## Usage
16
+ *
17
+ * ```tsx
18
+ * import { DefaultStateRenderer } from "@copilotkit/react-ui";
19
+ * import "@copilotkit/react-ui/styles.css";
20
+ *
21
+ * // Basic usage
22
+ * <DefaultStateRenderer
23
+ * state={agentState}
24
+ * status="inProgress"
25
+ * />
26
+ * ```
27
+ *
28
+ * ## Customization
29
+ *
30
+ * You can customize the appearance and behavior of the component:
31
+ *
32
+ * ```tsx
33
+ * // Custom labels and styling
34
+ * <DefaultStateRenderer
35
+ * state={agentState}
36
+ * status="inProgress"
37
+ * labels={{
38
+ * inProgressLabel: "Thinking...",
39
+ * completeLabel: "Finished processing",
40
+ * emptyLabel: "Nothing to show"
41
+ * }}
42
+ * className="my-custom-state"
43
+ * contentClassName="my-custom-content"
44
+ * itemClassName="my-custom-item"
45
+ * maxHeight="300px"
46
+ * defaultCollapsed={false}
47
+ * />
48
+ *
49
+ * // Custom renderers
50
+ * <DefaultStateRenderer
51
+ * state={agentState}
52
+ * status="inProgress"
53
+ * StateItemRenderer={({ item, isNewest, className }) => (
54
+ * <MyCustomItemRenderer item={item} isNewest={isNewest} className={className} />
55
+ * )}
56
+ * SkeletonLoader={({ className }) => (
57
+ * <MyCustomSkeletonLoader className={className} />
58
+ * )}
59
+ * />
60
+ * ```
61
+ *
62
+ * ### Look & Feel
63
+ *
64
+ * By default, CopilotKit components do not have any styles. You can import CopilotKit's stylesheet at the root of your project:
65
+ * ```tsx title="YourRootComponent.tsx"
66
+ * ...
67
+ * import "@copilotkit/react-ui/styles.css"; // [!code highlight]
68
+ *
69
+ * export function YourRootComponent() {
70
+ * return (
71
+ * <CopilotKit>
72
+ * ...
73
+ * </CopilotKit>
74
+ * );
75
+ * }
76
+ * ```
77
+ * For more information about how to customize the styles, check out the [Customize Look & Feel](/guides/custom-look-and-feel/customize-built-in-ui-components) guide.
78
+ */
79
+
80
+ import React, { useState, useRef, useEffect, useMemo } from "react";
81
+ import {
82
+ AgentState,
83
+ StateRendererProps,
84
+ StateItemRendererProps,
85
+ SkeletonLoaderProps,
86
+ ToolStateItem,
87
+ TaskStateItem,
88
+ ResponseRendererIconProps,
89
+ } from "./types";
90
+
91
+ /**
92
+ * Default expand icon component
93
+ */
94
+ const DefaultExpandIcon: React.FC<ResponseRendererIconProps> = ({ className }) => (
95
+ <svg
96
+ className={className}
97
+ width="16"
98
+ height="16"
99
+ viewBox="0 0 24 24"
100
+ fill="none"
101
+ stroke="currentColor"
102
+ strokeWidth="2"
103
+ strokeLinecap="round"
104
+ strokeLinejoin="round"
105
+ >
106
+ <polyline points="6 9 12 15 18 9"></polyline>
107
+ </svg>
108
+ );
109
+
110
+ /**
111
+ * Default collapse icon component
112
+ */
113
+ const DefaultCollapseIcon: React.FC<ResponseRendererIconProps> = ({ className }) => (
114
+ <svg
115
+ className={className}
116
+ width="16"
117
+ height="16"
118
+ viewBox="0 0 24 24"
119
+ fill="none"
120
+ stroke="currentColor"
121
+ strokeWidth="2"
122
+ strokeLinecap="round"
123
+ strokeLinejoin="round"
124
+ >
125
+ <polyline points="18 15 12 9 6 15"></polyline>
126
+ </svg>
127
+ );
128
+
129
+ /**
130
+ * Default loader icon component
131
+ */
132
+ const DefaultLoaderIcon: React.FC<ResponseRendererIconProps> = ({ className }) => (
133
+ <svg
134
+ className={`${className} copilotkit-spinner`}
135
+ width="16"
136
+ height="16"
137
+ viewBox="0 0 24 24"
138
+ fill="none"
139
+ stroke="currentColor"
140
+ strokeWidth="2"
141
+ strokeLinecap="round"
142
+ strokeLinejoin="round"
143
+ >
144
+ <path d="M21 12a9 9 0 1 1-6.219-8.56"></path>
145
+ </svg>
146
+ );
147
+
148
+ /**
149
+ * Helper to safely format content
150
+ */
151
+ const formatContent = (result: unknown): string => {
152
+ if (result === null || result === undefined) return "";
153
+ return typeof result === "string" ? result : JSON.stringify(result, null, 2);
154
+ };
155
+
156
+ /**
157
+ * Default skeleton loader component
158
+ */
159
+ const DefaultSkeletonLoader: React.FC<SkeletonLoaderProps> = ({ className }) => (
160
+ <div className={className || "copilotkit-skeleton"}>
161
+ <div className="copilotkit-skeleton-header">
162
+ <div className="copilotkit-skeleton-title"></div>
163
+ <div className="copilotkit-skeleton-subtitle"></div>
164
+ </div>
165
+ <div className="copilotkit-skeleton-content"></div>
166
+ </div>
167
+ );
168
+
169
+ /**
170
+ * Default state item renderer component
171
+ */
172
+ const DefaultStateItemRenderer: React.FC<StateItemRendererProps> = ({
173
+ item,
174
+ isNewest,
175
+ className,
176
+ }) => (
177
+ <div
178
+ className={`${className || "copilotkit-state-item"} ${isNewest ? "copilotkit-state-item-newest" : ""}`}
179
+ >
180
+ <div className="copilotkit-state-item-header">{"tool" in item ? item.tool : item.name}</div>
181
+
182
+ {"thought" in item && item.thought && (
183
+ <div className="copilotkit-state-item-thought">{item.thought}</div>
184
+ )}
185
+
186
+ {"result" in item && item.result !== undefined && item.result !== null && (
187
+ <div className="copilotkit-state-item-result">{formatContent(item.result)}</div>
188
+ )}
189
+
190
+ {"description" in item && item.description && (
191
+ <div className="copilotkit-state-item-description">{item.description}</div>
192
+ )}
193
+ </div>
194
+ );
195
+
196
+ /**
197
+ * Default state renderer component
198
+ */
199
+ export const DefaultStateRenderer: React.FC<StateRendererProps> = ({
200
+ state,
201
+ status,
202
+ StateItemRenderer = DefaultStateItemRenderer,
203
+ SkeletonLoader = DefaultSkeletonLoader,
204
+ labels,
205
+ icons,
206
+ className = "copilotkit-state",
207
+ contentClassName = "copilotkit-state-content",
208
+ itemClassName = "copilotkit-state-item",
209
+ maxHeight = "250px",
210
+ defaultCollapsed = true,
211
+ }) => {
212
+ const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
213
+ const contentRef = useRef<HTMLDivElement>(null);
214
+ const prevItemsLengthRef = useRef<number>(0);
215
+ const [newestItemId, setNewestItemId] = useState<string | null>(null);
216
+
217
+ // Default label values
218
+ const defaultLabels = {
219
+ inProgressLabel: "Analyzing",
220
+ completeLabel: "Analyzed",
221
+ emptyLabel: "No activity",
222
+ };
223
+
224
+ // Merge provided labels with defaults
225
+ const mergedLabels = { ...defaultLabels, ...labels };
226
+
227
+ // Decide which icon to display
228
+ const ExpandIcon = icons?.expand || DefaultExpandIcon;
229
+ const CollapseIcon = icons?.collapse || DefaultCollapseIcon;
230
+
231
+ // Safely compute derived values using useMemo
232
+ const items = useMemo(() => {
233
+ return state
234
+ ? [...(state.steps?.filter((s) => s.tool) || []), ...(state.tasks || [])].sort(
235
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
236
+ )
237
+ : [];
238
+ }, [state]);
239
+
240
+ // Determine if we should show skeleton UI (thinking but no items yet)
241
+ const isThinking = status === "inProgress" && items.length === 0;
242
+
243
+ // Track newest item and auto-scroll
244
+ useEffect(() => {
245
+ if (!state) return; // Skip effect if no state
246
+
247
+ // If new items were added
248
+ if (items.length > prevItemsLengthRef.current) {
249
+ // Get the newest item
250
+ if (items.length > 0) {
251
+ const newest = items[items.length - 1];
252
+ setNewestItemId(newest.id);
253
+
254
+ // Clear the animation after 1.5 seconds
255
+ setTimeout(() => {
256
+ setNewestItemId(null);
257
+ }, 1500);
258
+ }
259
+
260
+ // Auto-scroll to bottom
261
+ if (contentRef.current && !isCollapsed) {
262
+ contentRef.current.scrollTop = contentRef.current.scrollHeight;
263
+ }
264
+ }
265
+
266
+ prevItemsLengthRef.current = items.length;
267
+ }, [items, isCollapsed, state]);
268
+
269
+ // Early return for loading state with no state
270
+ if (!state) {
271
+ return (
272
+ <div className={className}>
273
+ <DefaultLoaderIcon className="copilotkit-loader" />
274
+ </div>
275
+ );
276
+ }
277
+
278
+ // Don't render anything if collapsed and empty and not thinking
279
+ if (isCollapsed && items.length === 0 && !isThinking) return null;
280
+
281
+ return (
282
+ <div className={className}>
283
+ {/* Header with toggle */}
284
+ <div className="copilotkit-state-header" onClick={() => setIsCollapsed(!isCollapsed)}>
285
+ {isCollapsed ? (
286
+ <ExpandIcon className="copilotkit-icon" />
287
+ ) : (
288
+ <CollapseIcon className="copilotkit-icon" />
289
+ )}
290
+ <div className="copilotkit-state-label">
291
+ {status === "inProgress" ? (
292
+ <span className="copilotkit-state-label-loading">{mergedLabels.inProgressLabel}</span>
293
+ ) : (
294
+ mergedLabels.completeLabel
295
+ )}
296
+ </div>
297
+ </div>
298
+
299
+ {/* Content area */}
300
+ {!isCollapsed && (
301
+ <div ref={contentRef} className={contentClassName} style={{ maxHeight }}>
302
+ {/* Render items if available */}
303
+ {items.length > 0 ? (
304
+ items.map((item) => (
305
+ <StateItemRenderer
306
+ key={item.id}
307
+ item={item}
308
+ isNewest={item.id === newestItemId}
309
+ className={itemClassName}
310
+ />
311
+ ))
312
+ ) : isThinking ? (
313
+ // Show skeleton loader while thinking
314
+ <>
315
+ <SkeletonLoader />
316
+ <SkeletonLoader />
317
+ </>
318
+ ) : (
319
+ // Empty state
320
+ <div className="copilotkit-state-empty">{mergedLabels.emptyLabel}</div>
321
+ )}
322
+ </div>
323
+ )}
324
+ </div>
325
+ );
326
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./DefaultResponseRenderer";
2
+ export * from "./DefaultStateRenderer";
3
+ export * from "./types";