@copilotkit/react-core 1.57.3 → 1.58.0-canary.thread-id-propagation

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 (292) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{copilotkit-CtXcs1ea.cjs → copilotkit-B4ouY7qC.cjs} +14 -3
  3. package/dist/copilotkit-B4ouY7qC.cjs.map +1 -0
  4. package/dist/copilotkit-BK9CVq9A.d.cts.map +1 -1
  5. package/dist/{copilotkit-CC8DjOiC.mjs → copilotkit-L4mM_JqG.mjs} +14 -3
  6. package/dist/copilotkit-L4mM_JqG.mjs.map +1 -0
  7. package/dist/copilotkit-WlmeVijs.d.mts.map +1 -1
  8. package/dist/index.cjs +3 -77
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.mts +2 -2
  12. package/dist/index.mjs +3 -77
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/index.umd.js +15 -78
  15. package/dist/index.umd.js.map +1 -1
  16. package/dist/v2/headless.cjs +11 -0
  17. package/dist/v2/headless.cjs.map +1 -1
  18. package/dist/v2/headless.d.cts.map +1 -1
  19. package/dist/v2/headless.d.mts.map +1 -1
  20. package/dist/v2/headless.mjs +11 -0
  21. package/dist/v2/headless.mjs.map +1 -1
  22. package/dist/v2/index.cjs +1 -1
  23. package/dist/v2/index.mjs +1 -1
  24. package/dist/v2/index.umd.js +13 -2
  25. package/dist/v2/index.umd.js.map +1 -1
  26. package/package.json +12 -13
  27. package/skills/react-core/SKILL.md +108 -0
  28. package/skills/react-core/references/agent-access.md +288 -0
  29. package/skills/react-core/references/attachments.md +291 -0
  30. package/skills/react-core/references/capabilities.md +138 -0
  31. package/skills/react-core/references/chat-components.md +221 -0
  32. package/skills/react-core/references/client-side-tools.md +358 -0
  33. package/skills/react-core/references/custom-message-renderers.md +226 -0
  34. package/skills/react-core/references/debug-mode.md +153 -0
  35. package/skills/react-core/references/human-in-the-loop.md +312 -0
  36. package/skills/react-core/references/provider-setup.md +326 -0
  37. package/skills/react-core/references/rendering-activity-messages.md +207 -0
  38. package/skills/react-core/references/rendering-tool-calls.md +319 -0
  39. package/skills/react-core/references/suggestions.md +211 -0
  40. package/skills/react-core/references/switching-agents-recipes.md +160 -0
  41. package/skills/react-core/references/switching-agents.md +231 -0
  42. package/skills/react-core/references/threads.md +226 -0
  43. package/.attw.json +0 -3
  44. package/CHANGELOG.md +0 -5043
  45. package/dist/copilotkit-CC8DjOiC.mjs.map +0 -1
  46. package/dist/copilotkit-CtXcs1ea.cjs.map +0 -1
  47. package/scripts/scope-preflight.mjs +0 -100
  48. package/src/components/CopilotListeners.tsx +0 -137
  49. package/src/components/__tests__/CopilotListeners.test.tsx +0 -38
  50. package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +0 -92
  51. package/src/components/copilot-provider/__tests__/copilotkit-error.test.tsx +0 -77
  52. package/src/components/copilot-provider/__tests__/error-visibility-prod.test.tsx +0 -70
  53. package/src/components/copilot-provider/__tests__/v1-explicit-threadid-bridge.test.tsx +0 -107
  54. package/src/components/copilot-provider/copilot-messages.tsx +0 -314
  55. package/src/components/copilot-provider/copilotkit-props.tsx +0 -214
  56. package/src/components/copilot-provider/copilotkit.tsx +0 -853
  57. package/src/components/copilot-provider/index.ts +0 -3
  58. package/src/components/dev-console/console-trigger.tsx +0 -283
  59. package/src/components/dev-console/developer-console-modal.tsx +0 -1016
  60. package/src/components/dev-console/icons.tsx +0 -106
  61. package/src/components/error-boundary/error-boundary.tsx +0 -99
  62. package/src/components/error-boundary/error-utils.tsx +0 -105
  63. package/src/components/index.ts +0 -1
  64. package/src/components/toast/exclamation-mark-icon.tsx +0 -27
  65. package/src/components/toast/toast-provider.tsx +0 -448
  66. package/src/components/usage-banner.tsx +0 -266
  67. package/src/context/__tests__/threads-context.test.tsx +0 -141
  68. package/src/context/coagent-state-renders-context.tsx +0 -89
  69. package/src/context/copilot-context.tsx +0 -365
  70. package/src/context/copilot-messages-context.tsx +0 -35
  71. package/src/context/index.ts +0 -22
  72. package/src/context/threads-context.tsx +0 -69
  73. package/src/hooks/__tests__/use-coagent-config.test.ts +0 -352
  74. package/src/hooks/__tests__/use-coagent-state-render-bridge.helpers.test.ts +0 -107
  75. package/src/hooks/__tests__/use-coagent-state-render.e2e.test.tsx +0 -1209
  76. package/src/hooks/__tests__/use-coagent-state-render.test.tsx +0 -356
  77. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +0 -241
  78. package/src/hooks/__tests__/use-frontend-tool-available.test.tsx +0 -72
  79. package/src/hooks/__tests__/use-frontend-tool-remount.e2e.test.tsx +0 -102
  80. package/src/hooks/index.ts +0 -33
  81. package/src/hooks/use-agent-nodename.ts +0 -33
  82. package/src/hooks/use-coagent-state-render-bridge.helpers.ts +0 -345
  83. package/src/hooks/use-coagent-state-render-bridge.tsx +0 -222
  84. package/src/hooks/use-coagent-state-render-registry.ts +0 -230
  85. package/src/hooks/use-coagent-state-render.ts +0 -163
  86. package/src/hooks/use-coagent.ts +0 -377
  87. package/src/hooks/use-configure-chat-suggestions.tsx +0 -96
  88. package/src/hooks/use-copilot-action.ts +0 -245
  89. package/src/hooks/use-copilot-additional-instructions.ts +0 -98
  90. package/src/hooks/use-copilot-authenticated-action.ts +0 -73
  91. package/src/hooks/use-copilot-chat-headless_c.ts +0 -264
  92. package/src/hooks/use-copilot-chat-suggestions.tsx +0 -134
  93. package/src/hooks/use-copilot-chat.ts +0 -132
  94. package/src/hooks/use-copilot-chat_internal.ts +0 -875
  95. package/src/hooks/use-copilot-readable.ts +0 -135
  96. package/src/hooks/use-copilot-runtime-client.ts +0 -178
  97. package/src/hooks/use-default-tool.ts +0 -13
  98. package/src/hooks/use-flat-category-store.ts +0 -109
  99. package/src/hooks/use-frontend-tool.ts +0 -113
  100. package/src/hooks/use-human-in-the-loop.ts +0 -138
  101. package/src/hooks/use-langgraph-interrupt.ts +0 -103
  102. package/src/hooks/use-lazy-tool-renderer.tsx +0 -30
  103. package/src/hooks/use-make-copilot-document-readable.ts +0 -30
  104. package/src/hooks/use-render-tool-call.ts +0 -89
  105. package/src/hooks/use-tree.ts +0 -222
  106. package/src/index.tsx +0 -7
  107. package/src/lib/copilot-task.ts +0 -215
  108. package/src/lib/index.ts +0 -1
  109. package/src/lib/status-checker.ts +0 -67
  110. package/src/setupTests.ts +0 -37
  111. package/src/test-helpers/copilot-context.ts +0 -91
  112. package/src/types/chat-suggestion-configuration.ts +0 -23
  113. package/src/types/coagent-action.ts +0 -35
  114. package/src/types/coagent-state.ts +0 -13
  115. package/src/types/crew.ts +0 -89
  116. package/src/types/document-pointer.ts +0 -7
  117. package/src/types/frontend-action.ts +0 -213
  118. package/src/types/index.ts +0 -17
  119. package/src/types/interrupt-action.ts +0 -58
  120. package/src/types/system-message.ts +0 -4
  121. package/src/utils/dev-console.ts +0 -19
  122. package/src/utils/index.ts +0 -2
  123. package/src/utils/suggestions-constants.ts +0 -8
  124. package/src/utils/utils.test.ts +0 -7
  125. package/src/utils/utils.ts +0 -6
  126. package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +0 -240
  127. package/src/v2/__tests__/globalSetup.ts +0 -14
  128. package/src/v2/__tests__/setup.ts +0 -93
  129. package/src/v2/__tests__/utils/test-helpers.tsx +0 -570
  130. package/src/v2/a2ui/A2UICatalogContext.tsx +0 -79
  131. package/src/v2/a2ui/A2UIMessageRenderer.tsx +0 -294
  132. package/src/v2/a2ui/A2UIToolCallRenderer.tsx +0 -290
  133. package/src/v2/components/CopilotKitInspector.tsx +0 -52
  134. package/src/v2/components/MCPAppsActivityRenderer.tsx +0 -815
  135. package/src/v2/components/OpenGenerativeUIRenderer.tsx +0 -598
  136. package/src/v2/components/WildcardToolCallRender.tsx +0 -86
  137. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +0 -665
  138. package/src/v2/components/chat/CopilotChat.tsx +0 -664
  139. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +0 -393
  140. package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +0 -374
  141. package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +0 -159
  142. package/src/v2/components/chat/CopilotChatAudioRecorder.tsx +0 -350
  143. package/src/v2/components/chat/CopilotChatInput.tsx +0 -1412
  144. package/src/v2/components/chat/CopilotChatMessageView.tsx +0 -716
  145. package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +0 -265
  146. package/src/v2/components/chat/CopilotChatSuggestionPill.tsx +0 -59
  147. package/src/v2/components/chat/CopilotChatSuggestionView.tsx +0 -134
  148. package/src/v2/components/chat/CopilotChatToggleButton.tsx +0 -171
  149. package/src/v2/components/chat/CopilotChatToolCallsView.tsx +0 -40
  150. package/src/v2/components/chat/CopilotChatUserMessage.tsx +0 -445
  151. package/src/v2/components/chat/CopilotChatView.tsx +0 -890
  152. package/src/v2/components/chat/CopilotModalHeader.tsx +0 -129
  153. package/src/v2/components/chat/CopilotPopup.tsx +0 -81
  154. package/src/v2/components/chat/CopilotPopupView.tsx +0 -317
  155. package/src/v2/components/chat/CopilotSidebar.tsx +0 -80
  156. package/src/v2/components/chat/CopilotSidebarView.tsx +0 -269
  157. package/src/v2/components/chat/Lightbox.tsx +0 -103
  158. package/src/v2/components/chat/__tests__/CopilotChat.absentThreadConnect.test.tsx +0 -66
  159. package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +0 -168
  160. package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +0 -1239
  161. package/src/v2/components/chat/__tests__/CopilotChat.onError.test.tsx +0 -73
  162. package/src/v2/components/chat/__tests__/CopilotChat.slots.e2e.test.tsx +0 -432
  163. package/src/v2/components/chat/__tests__/CopilotChat.suggestionsAlways.test.tsx +0 -183
  164. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +0 -184
  165. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +0 -649
  166. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.tsx +0 -624
  167. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +0 -702
  168. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.thumbs.test.tsx +0 -72
  169. package/src/v2/components/chat/__tests__/CopilotChatCopyButton.clipboard.test.tsx +0 -241
  170. package/src/v2/components/chat/__tests__/CopilotChatCssClasses.test.tsx +0 -107
  171. package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.tsx +0 -929
  172. package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +0 -1567
  173. package/src/v2/components/chat/__tests__/CopilotChatMessageView.slots.e2e.test.tsx +0 -1004
  174. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.tsx +0 -279
  175. package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +0 -336
  176. package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +0 -249
  177. package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.tsx +0 -530
  178. package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +0 -785
  179. package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +0 -2416
  180. package/src/v2/components/chat/__tests__/CopilotChatUserMessage.slots.e2e.test.tsx +0 -621
  181. package/src/v2/components/chat/__tests__/CopilotChatView.connectingGate.test.tsx +0 -56
  182. package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.tsx +0 -264
  183. package/src/v2/components/chat/__tests__/CopilotChatView.onClick.e2e.test.tsx +0 -853
  184. package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx +0 -94
  185. package/src/v2/components/chat/__tests__/CopilotChatView.slots.e2e.test.tsx +0 -1050
  186. package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.tsx +0 -484
  187. package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.tsx +0 -612
  188. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.tsx +0 -159
  189. package/src/v2/components/chat/__tests__/CopilotSidebarView.slots.e2e.test.tsx +0 -502
  190. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +0 -1068
  191. package/src/v2/components/chat/__tests__/MCPAppsProxy.e2e.test.tsx +0 -589
  192. package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +0 -403
  193. package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +0 -137
  194. package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +0 -37
  195. package/src/v2/components/chat/__tests__/setup.ts +0 -1
  196. package/src/v2/components/chat/index.ts +0 -90
  197. package/src/v2/components/chat/last-user-message-context.ts +0 -21
  198. package/src/v2/components/chat/normalize-auto-scroll.ts +0 -17
  199. package/src/v2/components/chat/scroll-element-context.ts +0 -13
  200. package/src/v2/components/index.ts +0 -8
  201. package/src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx +0 -286
  202. package/src/v2/components/intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx +0 -464
  203. package/src/v2/components/intelligence-indicator/index.ts +0 -2
  204. package/src/v2/components/license-warning-banner.tsx +0 -217
  205. package/src/v2/components/ui/button.tsx +0 -124
  206. package/src/v2/components/ui/dropdown-menu.tsx +0 -258
  207. package/src/v2/components/ui/tooltip.tsx +0 -60
  208. package/src/v2/context.ts +0 -62
  209. package/src/v2/headless.ts +0 -64
  210. package/src/v2/hooks/__tests__/standard-schema-types.test.tsx +0 -152
  211. package/src/v2/hooks/__tests__/standard-schema.test.tsx +0 -282
  212. package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +0 -140
  213. package/src/v2/hooks/__tests__/use-agent-context.test.tsx +0 -401
  214. package/src/v2/hooks/__tests__/use-agent-error-state.test.tsx +0 -44
  215. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +0 -211
  216. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +0 -1029
  217. package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +0 -159
  218. package/src/v2/hooks/__tests__/use-attachments.test.tsx +0 -169
  219. package/src/v2/hooks/__tests__/use-capabilities.test.tsx +0 -76
  220. package/src/v2/hooks/__tests__/use-component.test.tsx +0 -126
  221. package/src/v2/hooks/__tests__/use-configure-suggestions.e2e.test.tsx +0 -696
  222. package/src/v2/hooks/__tests__/use-default-render-tool.test.tsx +0 -153
  223. package/src/v2/hooks/__tests__/use-frontend-tool-available.test.tsx +0 -167
  224. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +0 -2148
  225. package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.tsx +0 -1261
  226. package/src/v2/hooks/__tests__/use-interrupt.test.tsx +0 -397
  227. package/src/v2/hooks/__tests__/use-katex-styles.test.tsx +0 -56
  228. package/src/v2/hooks/__tests__/use-keyboard-height.test.tsx +0 -192
  229. package/src/v2/hooks/__tests__/use-pin-to-send.test.tsx +0 -219
  230. package/src/v2/hooks/__tests__/use-render-custom-messages.test.tsx +0 -55
  231. package/src/v2/hooks/__tests__/use-render-tool.test.tsx +0 -259
  232. package/src/v2/hooks/__tests__/use-suggestions.e2e.test.tsx +0 -524
  233. package/src/v2/hooks/__tests__/use-threads.test.tsx +0 -757
  234. package/src/v2/hooks/__tests__/zod-regression.test.tsx +0 -311
  235. package/src/v2/hooks/index.ts +0 -24
  236. package/src/v2/hooks/use-agent-context.tsx +0 -45
  237. package/src/v2/hooks/use-agent.tsx +0 -227
  238. package/src/v2/hooks/use-attachments.tsx +0 -269
  239. package/src/v2/hooks/use-capabilities.tsx +0 -25
  240. package/src/v2/hooks/use-component.tsx +0 -91
  241. package/src/v2/hooks/use-configure-suggestions.tsx +0 -236
  242. package/src/v2/hooks/use-default-render-tool.tsx +0 -271
  243. package/src/v2/hooks/use-frontend-tool.tsx +0 -46
  244. package/src/v2/hooks/use-human-in-the-loop.tsx +0 -81
  245. package/src/v2/hooks/use-interrupt.tsx +0 -305
  246. package/src/v2/hooks/use-keyboard-height.tsx +0 -67
  247. package/src/v2/hooks/use-pin-to-send.ts +0 -94
  248. package/src/v2/hooks/use-render-activity-message.tsx +0 -72
  249. package/src/v2/hooks/use-render-custom-messages.tsx +0 -93
  250. package/src/v2/hooks/use-render-tool-call.tsx +0 -208
  251. package/src/v2/hooks/use-render-tool.tsx +0 -184
  252. package/src/v2/hooks/use-suggestions.tsx +0 -91
  253. package/src/v2/hooks/use-threads.tsx +0 -325
  254. package/src/v2/hooks/useKatexStyles.ts +0 -27
  255. package/src/v2/index.css +0 -1
  256. package/src/v2/index.ts +0 -27
  257. package/src/v2/lib/__tests__/completePartialMarkdown.test.ts +0 -495
  258. package/src/v2/lib/__tests__/processPartialHtml.test.ts +0 -112
  259. package/src/v2/lib/__tests__/renderSlot.test.tsx +0 -588
  260. package/src/v2/lib/__tests__/slots.test.ts +0 -56
  261. package/src/v2/lib/processPartialHtml.ts +0 -45
  262. package/src/v2/lib/react-core.ts +0 -156
  263. package/src/v2/lib/slots.tsx +0 -184
  264. package/src/v2/lib/transcription-client.ts +0 -184
  265. package/src/v2/lib/utils.ts +0 -8
  266. package/src/v2/providers/CopilotChatConfigurationProvider.tsx +0 -196
  267. package/src/v2/providers/CopilotKitProvider.tsx +0 -800
  268. package/src/v2/providers/SandboxFunctionsContext.ts +0 -10
  269. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +0 -652
  270. package/src/v2/providers/__tests__/CopilotKitProvider.license.test.tsx +0 -101
  271. package/src/v2/providers/__tests__/CopilotKitProvider.onError.test.tsx +0 -69
  272. package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.tsx +0 -881
  273. package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +0 -198
  274. package/src/v2/providers/__tests__/CopilotKitProvider.stability.test.tsx +0 -740
  275. package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +0 -713
  276. package/src/v2/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +0 -294
  277. package/src/v2/providers/index.ts +0 -21
  278. package/src/v2/styles/globals.css +0 -349
  279. package/src/v2/types/__tests__/defineToolCallRenderer.test.tsx +0 -525
  280. package/src/v2/types/defineToolCallRenderer.ts +0 -68
  281. package/src/v2/types/frontend-tool.ts +0 -8
  282. package/src/v2/types/human-in-the-loop.ts +0 -33
  283. package/src/v2/types/index.ts +0 -8
  284. package/src/v2/types/interrupt.ts +0 -15
  285. package/src/v2/types/react-activity-message-renderer.ts +0 -27
  286. package/src/v2/types/react-custom-message-renderer.ts +0 -17
  287. package/src/v2/types/react-tool-call-renderer.ts +0 -35
  288. package/src/v2/types/sandbox-function.ts +0 -11
  289. package/tsconfig.json +0 -8
  290. package/tsdown.config.ts +0 -193
  291. package/typedoc.json +0 -4
  292. package/vitest.config.mjs +0 -31
@@ -1,588 +0,0 @@
1
- import React, { forwardRef, useImperativeHandle, useRef } from "react";
2
- import { render, fireEvent, screen } from "@testing-library/react";
3
- import { vi } from "vitest";
4
- import "@testing-library/jest-dom";
5
- import { renderSlot, SlotValue } from "../slots";
6
-
7
- // Extend HTMLAttributes to include data attributes
8
- interface ExtendedDivAttributes extends React.HTMLAttributes<HTMLDivElement> {
9
- [key: `data-${string}`]: string | null | undefined;
10
- }
11
-
12
- // Test components for various scenarios
13
- const SimpleDiv: React.FC<ExtendedDivAttributes> = ({
14
- className,
15
- children,
16
- ...props
17
- }) => (
18
- <div className={className} {...props}>
19
- {children}
20
- </div>
21
- );
22
-
23
- const ButtonWithClick: React.FC<
24
- React.ButtonHTMLAttributes<HTMLButtonElement>
25
- > = ({ onClick, className, children, ...props }) => (
26
- <button className={className} onClick={onClick} {...props}>
27
- {children}
28
- </button>
29
- );
30
-
31
- const ComponentWithContent: React.FC<{
32
- content: string;
33
- className?: string;
34
- }> = ({ content, className }) => <div className={className}>{content}</div>;
35
-
36
- const ForwardRefComponent = forwardRef<
37
- HTMLInputElement,
38
- React.InputHTMLAttributes<HTMLInputElement>
39
- >(({ className, ...props }, ref) => (
40
- <input ref={ref} className={className} {...props} />
41
- ));
42
-
43
- ForwardRefComponent.displayName = "ForwardRefComponent";
44
-
45
- interface CustomHandle {
46
- focus: () => void;
47
- getValue: () => string;
48
- }
49
-
50
- const ComponentWithImperativeHandle = forwardRef<
51
- CustomHandle,
52
- { value?: string; className?: string }
53
- >(({ value = "", className }, ref) => {
54
- const inputRef = useRef<HTMLInputElement>(null);
55
-
56
- useImperativeHandle(ref, () => ({
57
- focus: () => inputRef.current?.focus(),
58
- getValue: () => inputRef.current?.value || value,
59
- }));
60
-
61
- return <input ref={inputRef} defaultValue={value} className={className} />;
62
- });
63
-
64
- ComponentWithImperativeHandle.displayName = "ComponentWithImperativeHandle";
65
-
66
- describe("renderSlot", () => {
67
- describe("Basic slot value types", () => {
68
- test("renders default component when slot is undefined", () => {
69
- const element = renderSlot(undefined, SimpleDiv, {
70
- children: "test content",
71
- });
72
- const { container } = render(element);
73
-
74
- expect(container.firstChild).toHaveTextContent("test content");
75
- expect(container.firstChild?.nodeName).toBe("DIV");
76
- });
77
-
78
- test("uses string slot as className", () => {
79
- const element = renderSlot("bg-red-500 text-white", SimpleDiv, {
80
- children: "styled content",
81
- });
82
- const { container } = render(element);
83
-
84
- expect(container.firstChild).toHaveClass("bg-red-500", "text-white");
85
- expect(container.firstChild).toHaveTextContent("styled content");
86
- });
87
-
88
- test("renders custom component when slot is a function", () => {
89
- const CustomComponent: React.FC<{ children: React.ReactNode }> = ({
90
- children,
91
- }) => <span data-testid="custom">{children}</span>;
92
-
93
- const element = renderSlot(CustomComponent, SimpleDiv, {
94
- children: "custom content",
95
- });
96
- render(element);
97
-
98
- expect(screen.getByTestId("custom")).toHaveTextContent("custom content");
99
- });
100
-
101
- test("merges object slot props with base props", () => {
102
- const element = renderSlot(
103
- { className: "slot-class", "data-slot": "true" },
104
- SimpleDiv,
105
- { children: "merged content", "data-base": "true" },
106
- );
107
- const { container } = render(element);
108
-
109
- expect(container.firstChild).toHaveClass("slot-class");
110
- expect(container.firstChild).toHaveAttribute("data-slot", "true");
111
- expect(container.firstChild).toHaveAttribute("data-base", "true");
112
- expect(container.firstChild).toHaveTextContent("merged content");
113
- });
114
- });
115
-
116
- describe("className handling", () => {
117
- test("string slot merges with props className using twMerge", () => {
118
- const element = renderSlot("slot-class", SimpleDiv, {
119
- className: "props-class",
120
- children: "test",
121
- });
122
- const { container } = render(element);
123
-
124
- // twMerge combines both classes
125
- expect(container.firstChild).toHaveClass("slot-class");
126
- expect(container.firstChild).toHaveClass("props-class");
127
- });
128
-
129
- test("object slot className overrides props className", () => {
130
- const element = renderSlot({ className: "slot-class" }, SimpleDiv, {
131
- className: "props-class",
132
- children: "test",
133
- });
134
- const { container } = render(element);
135
-
136
- // Object slots use spread, so slot className overrides props className
137
- expect(container.firstChild).toHaveClass("slot-class");
138
- expect(container.firstChild).not.toHaveClass("props-class");
139
- });
140
-
141
- test("props className is used when slot has no className", () => {
142
- const element = renderSlot({ "data-test": "true" }, SimpleDiv, {
143
- className: "props-class",
144
- children: "test",
145
- });
146
- const { container } = render(element);
147
-
148
- expect(container.firstChild).toHaveClass("props-class");
149
- expect(container.firstChild).toHaveAttribute("data-test", "true");
150
- });
151
-
152
- test("empty string slot preserves props className", () => {
153
- const element = renderSlot("", SimpleDiv, {
154
- className: "props-class",
155
- children: "test",
156
- });
157
- const { container } = render(element);
158
-
159
- // Empty string with twMerge preserves the props className
160
- expect(container.firstChild).toHaveClass("props-class");
161
- });
162
- });
163
-
164
- describe("Event handling and callbacks", () => {
165
- test("passes click handlers correctly", () => {
166
- const mockClick = vi.fn();
167
- const element = renderSlot(undefined, ButtonWithClick, {
168
- onClick: mockClick,
169
- children: "Click me",
170
- });
171
-
172
- render(element);
173
- fireEvent.click(screen.getByRole("button"));
174
-
175
- expect(mockClick).toHaveBeenCalledTimes(1);
176
- });
177
-
178
- test("object slot can override event handlers", () => {
179
- const baseMockClick = vi.fn();
180
- const slotMockClick = vi.fn();
181
-
182
- const element = renderSlot({ onClick: slotMockClick }, ButtonWithClick, {
183
- onClick: baseMockClick,
184
- children: "Click me",
185
- });
186
-
187
- render(element);
188
- fireEvent.click(screen.getByRole("button"));
189
-
190
- expect(slotMockClick).toHaveBeenCalledTimes(1);
191
- expect(baseMockClick).not.toHaveBeenCalled();
192
- });
193
-
194
- test("custom component receives all event handlers", () => {
195
- const mockClick = vi.fn();
196
- const CustomButton: React.FC<
197
- React.ButtonHTMLAttributes<HTMLButtonElement>
198
- > = (props) => <button {...props} data-testid="custom-button" />;
199
-
200
- const element = renderSlot(CustomButton, ButtonWithClick, {
201
- onClick: mockClick,
202
- children: "Custom button",
203
- });
204
-
205
- render(element);
206
- fireEvent.click(screen.getByTestId("custom-button"));
207
-
208
- expect(mockClick).toHaveBeenCalledTimes(1);
209
- });
210
- });
211
-
212
- describe("Ref forwarding", () => {
213
- test("forwards refs to default component", () => {
214
- const ref = React.createRef<HTMLInputElement>();
215
- const element = renderSlot(undefined, ForwardRefComponent, { ref });
216
-
217
- render(element);
218
-
219
- expect(ref.current).toBeInstanceOf(HTMLInputElement);
220
- });
221
-
222
- test("forwards refs to custom component", () => {
223
- const ref = React.createRef<HTMLInputElement>();
224
- const CustomInput = forwardRef<
225
- HTMLInputElement,
226
- React.InputHTMLAttributes<HTMLInputElement>
227
- >((props, forwardedRef) => (
228
- <input {...props} ref={forwardedRef} data-testid="custom-input" />
229
- ));
230
-
231
- const element = renderSlot(CustomInput, ForwardRefComponent, { ref });
232
-
233
- render(element);
234
-
235
- expect(ref.current).toBeInstanceOf(HTMLInputElement);
236
- // Check if the custom component was actually used
237
- const customInput = screen.queryByTestId("custom-input");
238
- if (customInput) {
239
- expect(customInput).toBe(ref.current);
240
- } else {
241
- // If custom component wasn't used, this is a bug in renderSlot
242
- expect(ref.current).toBeInstanceOf(HTMLInputElement);
243
- }
244
- });
245
-
246
- test("works with useImperativeHandle", () => {
247
- const ref = React.createRef<CustomHandle>();
248
- const element = renderSlot(undefined, ComponentWithImperativeHandle, {
249
- ref,
250
- value: "test-value",
251
- });
252
-
253
- render(element);
254
-
255
- expect(ref.current?.getValue()).toBe("test-value");
256
- expect(typeof ref.current?.focus).toBe("function");
257
- });
258
- });
259
-
260
- describe("Complex prop merging", () => {
261
- test("deeply nested object props are merged correctly", () => {
262
- const ComplexComponent: React.FC<{
263
- config?: { theme: string; options: { debug: boolean } };
264
- className?: string;
265
- }> = ({ config, className }) => (
266
- <div className={className} data-config={JSON.stringify(config)}>
267
- Complex component
268
- </div>
269
- );
270
-
271
- const element = renderSlot(
272
- {
273
- config: { theme: "dark", options: { debug: true } },
274
- className: "slot-class",
275
- },
276
- ComplexComponent,
277
- {
278
- config: { theme: "light", options: { debug: false } },
279
- className: "base-class",
280
- },
281
- );
282
-
283
- const { container } = render(element);
284
- const configData = JSON.parse(
285
- (container.firstChild as Element)?.getAttribute("data-config") || "{}",
286
- );
287
-
288
- expect(configData.theme).toBe("dark"); // slot overrides base
289
- expect(configData.options.debug).toBe(true); // slot overrides base
290
- expect(container.firstChild).toHaveClass("slot-class");
291
- });
292
-
293
- test("handles undefined and null prop values", () => {
294
- const element = renderSlot(
295
- { title: undefined, "data-test": null },
296
- SimpleDiv,
297
- { title: "base-title", "data-base": "value", children: "test" },
298
- );
299
- const { container } = render(element);
300
-
301
- expect(container.firstChild).toHaveAttribute("data-base", "value");
302
-
303
- // Note: undefined in slot object overrides base props and removes them
304
- // This is expected JavaScript spread behavior
305
- expect(container.firstChild).not.toHaveAttribute("title");
306
- });
307
- });
308
-
309
- describe("Real-world usage patterns", () => {
310
- test("simulates CopilotChatInput Toolbar usage with twMerge pattern", () => {
311
- // This simulates the complex pattern in CopilotChatInput
312
- const Toolbar: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
313
- className,
314
- ...props
315
- }) => <div className={`base-toolbar ${className || ""}`} {...props} />;
316
-
317
- const toolbarSlot: SlotValue<typeof Toolbar> = "custom-toolbar-class";
318
-
319
- const element = renderSlot(toolbarSlot, Toolbar, {
320
- children: "toolbar content",
321
- });
322
-
323
- const { container } = render(element);
324
- expect(container.firstChild).toHaveClass("custom-toolbar-class");
325
- expect(container.firstChild).toHaveTextContent("toolbar content");
326
- });
327
-
328
- test("simulates CopilotChatAssistantMessage content passing", () => {
329
- const element = renderSlot(undefined, ComponentWithContent, {
330
- content: "message content",
331
- className: "message-class",
332
- });
333
-
334
- const { container } = render(element);
335
- expect(container.firstChild).toHaveTextContent("message content");
336
- expect(container.firstChild).toHaveClass("message-class");
337
- });
338
-
339
- test("simulates subcomponent property overrides", () => {
340
- // This simulates the pattern from CopilotChatMessageView where subcomponent props are overridden
341
- const SubComponent: React.FC<{
342
- label: string;
343
- disabled?: boolean;
344
- className?: string;
345
- }> = ({ label, disabled, className }) => (
346
- <button disabled={disabled} className={className}>
347
- {label}
348
- </button>
349
- );
350
-
351
- const element = renderSlot(
352
- { disabled: true, className: "override-class" },
353
- SubComponent,
354
- { label: "Click me", disabled: false, className: "base-class" },
355
- );
356
-
357
- render(element);
358
- const button = screen.getByRole("button");
359
-
360
- expect(button).toBeDisabled(); // slot overrides base
361
- expect(button).toHaveClass("override-class");
362
- expect(button).not.toHaveClass("base-class");
363
- expect(button).toHaveTextContent("Click me");
364
- });
365
- });
366
-
367
- describe("Edge cases and error scenarios", () => {
368
- test("handles React elements as slot values", () => {
369
- const reactElement = <div data-testid="react-element">React Element</div>;
370
-
371
- // React elements should be treated as objects, not functions
372
- const element = renderSlot(reactElement as any, SimpleDiv, {
373
- children: "fallback",
374
- });
375
-
376
- render(element);
377
-
378
- // Should render the default component since React elements are treated as objects
379
- expect(screen.queryByTestId("react-element")).not.toBeInTheDocument();
380
- });
381
-
382
- test("handles components with no props", () => {
383
- const NoPropsComponent: React.FC = () => <div>No props component</div>;
384
-
385
- const element = renderSlot(NoPropsComponent, SimpleDiv, {
386
- children: "test",
387
- });
388
-
389
- render(element);
390
- expect(screen.getByText("No props component")).toBeInTheDocument();
391
- });
392
-
393
- test("handles empty object slot", () => {
394
- const element = renderSlot({}, SimpleDiv, { children: "test content" });
395
- const { container } = render(element);
396
-
397
- expect(container.firstChild).toHaveTextContent("test content");
398
- });
399
-
400
- test("handles component with children render prop pattern", () => {
401
- const RenderPropComponent: React.FC<{
402
- children: (data: { count: number }) => React.ReactNode;
403
- className?: string;
404
- }> = ({ children, className }) => (
405
- <div className={className}>{children({ count: 5 })}</div>
406
- );
407
-
408
- const element = renderSlot(undefined, RenderPropComponent, {
409
- children: ({ count }: { count: number }) => <span>Count: {count}</span>,
410
- className: "render-prop-class",
411
- });
412
-
413
- const { container } = render(element);
414
- expect(container.firstChild).toHaveTextContent("Count: 5");
415
- expect(container.firstChild).toHaveClass("render-prop-class");
416
- });
417
-
418
- test("handles boolean and number props", () => {
419
- const ComponentWithBooleans: React.FC<{
420
- isVisible: boolean;
421
- count: number;
422
- className?: string;
423
- }> = ({ isVisible, count, className }) => (
424
- <div className={className}>
425
- {isVisible ? `Visible with count: ${count}` : "Hidden"}
426
- </div>
427
- );
428
-
429
- const element = renderSlot(
430
- { isVisible: false, count: 10 },
431
- ComponentWithBooleans,
432
- { isVisible: true, count: 5, className: "test-class" },
433
- );
434
-
435
- const { container } = render(element);
436
- expect(container.firstChild).toHaveTextContent("Hidden"); // slot overrides
437
- });
438
-
439
- test("handles array props", () => {
440
- const ComponentWithArray: React.FC<{
441
- items: string[];
442
- className?: string;
443
- }> = ({ items, className }) => (
444
- <ul className={className}>
445
- {items.map((item, index) => (
446
- <li key={index}>{item}</li>
447
- ))}
448
- </ul>
449
- );
450
-
451
- const element = renderSlot(
452
- { items: ["slot1", "slot2"] },
453
- ComponentWithArray,
454
- { items: ["base1", "base2"], className: "list-class" },
455
- );
456
-
457
- render(element);
458
- expect(screen.getByText("slot1")).toBeInTheDocument();
459
- expect(screen.getByText("slot2")).toBeInTheDocument();
460
- expect(screen.queryByText("base1")).not.toBeInTheDocument();
461
- });
462
- });
463
-
464
- describe("Performance and optimization", () => {
465
- test("does not recreate elements unnecessarily", () => {
466
- const renderSpy = vi.fn();
467
- const TrackedComponent: React.FC<{ value: string }> = ({ value }) => {
468
- renderSpy(value);
469
- return <div>{value}</div>;
470
- };
471
-
472
- const element1 = renderSlot(undefined, TrackedComponent, {
473
- value: "test",
474
- });
475
- const element2 = renderSlot(undefined, TrackedComponent, {
476
- value: "test",
477
- });
478
-
479
- render(element1);
480
- render(element2);
481
-
482
- expect(renderSpy).toHaveBeenCalledTimes(2);
483
- expect(renderSpy).toHaveBeenCalledWith("test");
484
- });
485
-
486
- test("handles large prop objects efficiently", () => {
487
- const largePropObject: Record<string, string> = {};
488
- for (let i = 0; i < 100; i++) {
489
- largePropObject[`prop${i}`] = `value${i}`;
490
- }
491
-
492
- const element = renderSlot({ className: "slot-class" }, SimpleDiv, {
493
- ...largePropObject,
494
- children: "test",
495
- });
496
-
497
- const { container } = render(element);
498
- expect(container.firstChild).toHaveClass("slot-class");
499
- expect(container.firstChild).toHaveTextContent("test");
500
- });
501
- });
502
-
503
- describe("Type compatibility", () => {
504
- test("preserves component prop types", () => {
505
- // This test ensures type safety is maintained
506
- const TypedComponent: React.FC<{
507
- requiredProp: string;
508
- optionalProp?: number;
509
- className?: string;
510
- }> = ({ requiredProp, optionalProp, className }) => (
511
- <div className={className}>
512
- {requiredProp} - {optionalProp}
513
- </div>
514
- );
515
-
516
- const element = renderSlot({ optionalProp: 42 }, TypedComponent, {
517
- requiredProp: "test",
518
- className: "typed-class",
519
- });
520
-
521
- const { container } = render(element);
522
- expect(container.firstChild).toHaveTextContent("test - 42");
523
- expect(container.firstChild).toHaveClass("typed-class");
524
- });
525
- });
526
-
527
- describe("Additional bug hunting", () => {
528
- test("function component slot should override default component", () => {
529
- const CustomComponent: React.FC<{ children: React.ReactNode }> = ({
530
- children,
531
- }) => <span data-testid="definitely-custom">{children}</span>;
532
-
533
- const element = renderSlot(CustomComponent, SimpleDiv, {
534
- children: "custom content",
535
- });
536
- render(element);
537
-
538
- const customElement = screen.queryByTestId("definitely-custom");
539
- if (customElement) {
540
- expect(customElement).toHaveTextContent("custom content");
541
- } else {
542
- // Fallback assertion to show what actually renders
543
- expect(screen.getByText("custom content")).toBeInTheDocument();
544
- }
545
- });
546
-
547
- test("React.createElement vs JSX differences", () => {
548
- // Test if there are differences between React.createElement and JSX rendering
549
- const TestComponent: React.FC<{ testProp: string }> = ({ testProp }) => (
550
- <div data-test-prop={testProp}>createElement test</div>
551
- );
552
-
553
- const element = renderSlot(undefined, TestComponent, {
554
- testProp: "test-value",
555
- });
556
- const { container } = render(element);
557
-
558
- expect(container.firstChild).toHaveAttribute(
559
- "data-test-prop",
560
- "test-value",
561
- );
562
- expect(container.firstChild).toHaveTextContent("createElement test");
563
- });
564
-
565
- test("nested component slot behavior", () => {
566
- const NestedComponent: React.FC<{ children: React.ReactNode }> = ({
567
- children,
568
- }) => (
569
- <div data-testid="nested-wrapper">
570
- <span data-testid="nested-inner">{children}</span>
571
- </div>
572
- );
573
-
574
- const element = renderSlot(NestedComponent, SimpleDiv, {
575
- children: "nested content",
576
- });
577
- render(element);
578
-
579
- // Check if nested structure is preserved
580
- const wrapper = screen.queryByTestId("nested-wrapper");
581
- const inner = screen.queryByTestId("nested-inner");
582
-
583
- if (wrapper && inner) {
584
- expect(inner).toHaveTextContent("nested content");
585
- }
586
- });
587
- });
588
- });
@@ -1,56 +0,0 @@
1
- import { renderHook } from "@testing-library/react";
2
- import { describe, it, expect } from "vitest";
3
- import { useShallowStableRef } from "../slots";
4
-
5
- describe("useShallowStableRef", () => {
6
- it("returns the same reference when called twice with shallowly equal plain objects", () => {
7
- const initial = { a: 1 };
8
- const { result, rerender } = renderHook(
9
- ({ value }: { value: { a: number } }) => useShallowStableRef(value),
10
- { initialProps: { value: initial } },
11
- );
12
-
13
- const firstRef = result.current;
14
- rerender({ value: { a: 1 } }); // new object, same shape
15
- expect(result.current).toBe(firstRef);
16
- });
17
-
18
- it("updates the reference when the value changes", () => {
19
- const { result, rerender } = renderHook(
20
- ({ value }: { value: { a: number } }) => useShallowStableRef(value),
21
- { initialProps: { value: { a: 1 } } },
22
- );
23
-
24
- const firstRef = result.current;
25
- rerender({ value: { a: 2 } });
26
- expect(result.current).not.toBe(firstRef);
27
- expect(result.current).toEqual({ a: 2 });
28
- });
29
-
30
- it("handles undefined without crashing", () => {
31
- const { result } = renderHook(() =>
32
- useShallowStableRef(undefined as unknown as { a: number }),
33
- );
34
- expect(result.current).toBeUndefined();
35
- });
36
-
37
- it("handles null without crashing", () => {
38
- const { result } = renderHook(() =>
39
- useShallowStableRef(null as unknown as { a: number }),
40
- );
41
- expect(result.current).toBeNull();
42
- });
43
-
44
- it("does not shallow-compare arrays — treats them by reference", () => {
45
- const arr1 = [1, 2, 3];
46
- const { result, rerender } = renderHook(
47
- ({ value }: { value: number[] }) => useShallowStableRef(value),
48
- { initialProps: { value: arr1 } },
49
- );
50
-
51
- const firstRef = result.current;
52
- rerender({ value: [1, 2, 3] }); // new array, same contents
53
- // arrays are not plain objects — reference should update
54
- expect(result.current).not.toBe(firstRef);
55
- });
56
- });
@@ -1,45 +0,0 @@
1
- /**
2
- * Extracts all complete `<style>` blocks from the raw HTML.
3
- * Returns the concatenated style tags, suitable for injection into `<head>`.
4
- */
5
- export function extractCompleteStyles(html: string): string {
6
- const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
7
- return matches ? matches.join("") : "";
8
- }
9
-
10
- /**
11
- * Processes raw accumulated HTML for safe preview via innerHTML injection.
12
- * Pure function, no DOM dependencies.
13
- *
14
- * Pipeline (order matters):
15
- * 1. Strip incomplete tag at end
16
- * 2. Strip complete <style>, <script>, and <head> blocks
17
- * 3. Strip incomplete <style>/<script>/<head> blocks
18
- * 4. Strip incomplete HTML entities
19
- * 5. Extract body content (or use full string if no <body>)
20
- */
21
- export function processPartialHtml(html: string): string {
22
- let result = html;
23
-
24
- // 1. Strip incomplete tag at end — e.g. `<div class="fo`
25
- result = result.replace(/<[^>]*$/, "");
26
-
27
- // 2. Strip complete <style>, <script>, and <head> blocks
28
- result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
29
-
30
- // 3. Strip incomplete <style>/<script>/<head> blocks (opening tag, no close)
31
- result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
32
-
33
- // 4. Strip incomplete HTML entities — e.g. `&amp` without semicolon
34
- result = result.replace(/&[a-zA-Z0-9#]*$/, "");
35
-
36
- // 5. Extract body content
37
- const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
38
- if (bodyMatch) {
39
- result = bodyMatch[1]!;
40
- // Strip </body> and everything after
41
- result = result.replace(/<\/body>[\s\S]*/i, "");
42
- }
43
-
44
- return result;
45
- }