@copilotkit/vue 2.0.0-rc.1 → 2.0.0-rc.2

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 (239) hide show
  1. package/PARITY.md +13 -12
  2. package/README.md +1 -2
  3. package/dist/components/copilot-provider/CopilotKit.vue.d.ts +3 -1
  4. package/dist/components/copilot-provider/CopilotKit.vue.d.ts.map +1 -1
  5. package/dist/index.cjs +43 -46
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.mjs +10170 -7506
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/v2/components/A2UIMessageRenderer.d.ts +7 -0
  10. package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
  11. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
  12. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +11 -2
  13. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
  14. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +136 -0
  15. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -0
  16. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +1 -1
  17. package/dist/v2/components/chat/CopilotChat.vue.d.ts +1 -0
  18. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  19. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +3 -1
  20. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  21. package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts +16 -0
  22. package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts.map +1 -0
  23. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  24. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  25. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
  26. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts +22 -0
  27. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -0
  28. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +69 -16
  29. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  30. package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts +12 -0
  31. package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts.map +1 -0
  32. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts +22 -0
  33. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -0
  34. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +20 -0
  35. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  36. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  37. package/dist/v2/components/chat/CopilotChatSuggestionPill.vue.d.ts.map +1 -1
  38. package/dist/v2/components/chat/CopilotChatSuggestionView.vue.d.ts.map +1 -1
  39. package/dist/v2/components/chat/CopilotChatToggleButton.vue.d.ts.map +1 -1
  40. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +32 -0
  41. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -0
  42. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  43. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +11 -1
  44. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  45. package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -1
  46. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -0
  47. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  48. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts +5 -0
  49. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -0
  50. package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
  51. package/dist/v2/components/chat/CopilotModalHeaderCloseButton.d.ts.map +1 -1
  52. package/dist/v2/components/chat/CopilotModalHeaderTitle.d.ts.map +1 -1
  53. package/dist/v2/components/chat/CopilotPopup.vue.d.ts +1 -0
  54. package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -1
  55. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +1 -0
  56. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -1
  57. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +1 -0
  58. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  59. package/dist/v2/components/chat/CopilotPopupWelcomeScreen.vue.d.ts.map +1 -1
  60. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +2 -0
  61. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -1
  62. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +2 -0
  63. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -1
  64. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +2 -0
  65. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  66. package/dist/v2/components/chat/CopilotSidebarWelcomeScreen.vue.d.ts.map +1 -1
  67. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
  68. package/dist/v2/components/chat/index.d.ts +17 -0
  69. package/dist/v2/components/chat/index.d.ts.map +1 -1
  70. package/dist/v2/components/chat/inspector-shortcuts.d.ts +12 -0
  71. package/dist/v2/components/chat/inspector-shortcuts.d.ts.map +1 -0
  72. package/dist/v2/components/chat/message-feedback.d.ts +89 -0
  73. package/dist/v2/components/chat/message-feedback.d.ts.map +1 -0
  74. package/dist/v2/components/chat/tool-call-capture.d.ts +39 -0
  75. package/dist/v2/components/chat/tool-call-capture.d.ts.map +1 -0
  76. package/dist/v2/components/chat/tool-call-display-context.d.ts +39 -0
  77. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -0
  78. package/dist/v2/components/chat/tool-call-group-parts.d.ts +144 -0
  79. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -0
  80. package/dist/v2/components/chat/tool-call-layout.d.ts +115 -0
  81. package/dist/v2/components/chat/tool-call-layout.d.ts.map +1 -0
  82. package/dist/v2/components/chat/turn-work.d.ts +35 -0
  83. package/dist/v2/components/chat/turn-work.d.ts.map +1 -0
  84. package/dist/v2/components/chat/types.d.ts +104 -4
  85. package/dist/v2/components/chat/types.d.ts.map +1 -1
  86. package/dist/v2/components/chat/use-lightbox.d.ts +18 -0
  87. package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -0
  88. package/dist/v2/components/icons/index.d.ts +1 -1
  89. package/dist/v2/components/icons/index.d.ts.map +1 -1
  90. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +55 -0
  91. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -0
  92. package/dist/v2/components/ui/CopilotPopover.vue.d.ts +40 -0
  93. package/dist/v2/components/ui/CopilotPopover.vue.d.ts.map +1 -0
  94. package/dist/v2/components/ui/CopilotTooltip.vue.d.ts +37 -0
  95. package/dist/v2/components/ui/CopilotTooltip.vue.d.ts.map +1 -0
  96. package/dist/v2/components/ui/dropdown-menu.d.ts +14 -0
  97. package/dist/v2/components/ui/dropdown-menu.d.ts.map +1 -0
  98. package/dist/v2/components/ui/floating.d.ts +84 -0
  99. package/dist/v2/components/ui/floating.d.ts.map +1 -0
  100. package/dist/v2/components/ui/presence.d.ts +9 -0
  101. package/dist/v2/components/ui/presence.d.ts.map +1 -0
  102. package/dist/v2/components/ui/use-id.d.ts +9 -0
  103. package/dist/v2/components/ui/use-id.d.ts.map +1 -0
  104. package/dist/v2/hooks/use-default-render-tool.d.ts +6 -0
  105. package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
  106. package/dist/v2/hooks/use-render-tool.d.ts +4 -0
  107. package/dist/v2/hooks/use-render-tool.d.ts.map +1 -1
  108. package/dist/v2/index.cjs +1 -1
  109. package/dist/v2/index.d.cts +3 -0
  110. package/dist/v2/index.d.mts +3 -0
  111. package/dist/v2/index.d.ts +3 -0
  112. package/dist/v2/index.d.ts.map +1 -1
  113. package/dist/v2/index.mjs +165 -162
  114. package/dist/v2/lib/message-feedback.d.ts +33 -0
  115. package/dist/v2/lib/message-feedback.d.ts.map +1 -0
  116. package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
  117. package/dist/v2/providers/CopilotKitProvider.types.d.ts +56 -1
  118. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  119. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +5 -1
  120. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  121. package/dist/v2/providers/index.d.ts +1 -1
  122. package/dist/v2/providers/index.d.ts.map +1 -1
  123. package/dist/v2/providers/keys.d.ts +8 -0
  124. package/dist/v2/providers/keys.d.ts.map +1 -1
  125. package/dist/v2/providers/types.d.ts +9 -0
  126. package/dist/v2/providers/types.d.ts.map +1 -1
  127. package/dist/v2/styles/CopilotKitStyles.d.ts +18 -0
  128. package/dist/v2/styles/CopilotKitStyles.d.ts.map +1 -0
  129. package/dist/v2/types/defineToolCallRenderer.d.ts +2 -0
  130. package/dist/v2/types/defineToolCallRenderer.d.ts.map +1 -1
  131. package/dist/v2/types/vue-tool-call-renderer.d.ts +7 -0
  132. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  133. package/package.json +11 -23
  134. package/src/components/copilot-provider/CopilotKit.vue +4 -1
  135. package/src/v2/__tests__/exports.test.ts +4 -2
  136. package/src/v2/components/A2UIMessageRenderer.ts +55 -67
  137. package/src/v2/components/A2UISurfaceActivityRenderer.vue +11 -23
  138. package/src/v2/components/__tests__/A2UIBasicCatalogColors.test.ts +8 -9
  139. package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +63 -36
  140. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +14 -257
  141. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +444 -0
  142. package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +175 -0
  143. package/src/v2/components/chat/BasicMarkdown.vue +8 -6
  144. package/src/v2/components/chat/CopilotChat.vue +7 -0
  145. package/src/v2/components/chat/CopilotChatAssistantMessage.vue +267 -67
  146. package/src/v2/components/chat/CopilotChatAssistantToolbarButton.vue +46 -0
  147. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +141 -168
  148. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +67 -18
  149. package/src/v2/components/chat/CopilotChatAudioRecorder.vue +2 -2
  150. package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +240 -0
  151. package/src/v2/components/chat/CopilotChatInput.vue +210 -161
  152. package/src/v2/components/chat/CopilotChatInspectorButton.vue +331 -0
  153. package/src/v2/components/chat/CopilotChatLightbox.vue +105 -0
  154. package/src/v2/components/chat/CopilotChatMessageView.vue +261 -18
  155. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +21 -22
  156. package/src/v2/components/chat/CopilotChatSuggestionPill.vue +5 -14
  157. package/src/v2/components/chat/CopilotChatSuggestionView.vue +1 -4
  158. package/src/v2/components/chat/CopilotChatToggleButton.vue +3 -9
  159. package/src/v2/components/chat/CopilotChatToggleButtonCloseIcon.ts +1 -1
  160. package/src/v2/components/chat/CopilotChatToggleButtonOpenIcon.ts +1 -1
  161. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +330 -0
  162. package/src/v2/components/chat/CopilotChatToolCallItem.vue +21 -11
  163. package/src/v2/components/chat/CopilotChatToolCallsView.vue +192 -21
  164. package/src/v2/components/chat/CopilotChatUserMessage.vue +105 -42
  165. package/src/v2/components/chat/CopilotChatView.vue +33 -38
  166. package/src/v2/components/chat/CopilotChatWorkSummary.vue +85 -0
  167. package/src/v2/components/chat/CopilotModalHeader.vue +10 -8
  168. package/src/v2/components/chat/CopilotModalHeaderCloseButton.ts +7 -6
  169. package/src/v2/components/chat/CopilotModalHeaderTitle.ts +1 -4
  170. package/src/v2/components/chat/CopilotPopup.vue +3 -0
  171. package/src/v2/components/chat/CopilotPopupView.vue +1 -0
  172. package/src/v2/components/chat/CopilotPopupViewInternal.vue +9 -19
  173. package/src/v2/components/chat/CopilotPopupWelcomeScreen.vue +5 -8
  174. package/src/v2/components/chat/CopilotSidebar.vue +5 -0
  175. package/src/v2/components/chat/CopilotSidebarView.vue +2 -0
  176. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -17
  177. package/src/v2/components/chat/CopilotSidebarWelcomeScreen.vue +7 -10
  178. package/src/v2/components/chat/StreamingMarkdownDefault.vue +3 -5
  179. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.inspector.test.ts +269 -0
  180. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.learningFeedback.test.ts +760 -0
  181. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.ts +3 -5
  182. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.ts +2 -2
  183. package/src/v2/components/chat/__tests__/CopilotChatInput.addMenu.test.ts +328 -0
  184. package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.ts +6 -3
  185. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +22 -13
  186. package/src/v2/components/chat/__tests__/CopilotChatLightbox.test.ts +160 -0
  187. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +735 -0
  188. package/src/v2/components/chat/__tests__/CopilotChatSuggestionPill.test.ts +1 -1
  189. package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.ts +3 -1
  190. package/src/v2/components/chat/__tests__/CopilotChatToolCallsView.learning.test.ts +75 -0
  191. package/src/v2/components/chat/__tests__/CopilotChatUserMessage.attachments.test.ts +143 -0
  192. package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.ts +5 -4
  193. package/src/v2/components/chat/__tests__/CopilotChatView.rootWidth.test.ts +7 -33
  194. package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.ts +1 -1
  195. package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +3 -3
  196. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +126 -0
  197. package/src/v2/components/chat/__tests__/messageFeedbackDefaults.test.ts +33 -0
  198. package/src/v2/components/chat/__tests__/modalChatDisplayProps.test.ts +44 -0
  199. package/src/v2/components/chat/index.ts +2 -0
  200. package/src/v2/components/chat/inspector-shortcuts.ts +34 -0
  201. package/src/v2/components/chat/message-feedback.ts +291 -0
  202. package/src/v2/components/chat/tool-call-capture.ts +55 -0
  203. package/src/v2/components/chat/tool-call-display-context.ts +49 -0
  204. package/src/v2/components/chat/tool-call-group-parts.ts +292 -0
  205. package/src/v2/components/chat/tool-call-layout.ts +304 -0
  206. package/src/v2/components/chat/turn-work.ts +61 -0
  207. package/src/v2/components/chat/types.ts +113 -3
  208. package/src/v2/components/chat/use-lightbox.ts +64 -0
  209. package/src/v2/components/icons/index.ts +4 -0
  210. package/src/v2/components/ui/CopilotDropdownMenuContent.vue +465 -0
  211. package/src/v2/components/ui/CopilotPopover.vue +138 -0
  212. package/src/v2/components/ui/CopilotTooltip.vue +237 -0
  213. package/src/v2/components/ui/__tests__/CopilotTooltip.test.ts +168 -0
  214. package/src/v2/components/ui/dropdown-menu.ts +15 -0
  215. package/src/v2/components/ui/floating.ts +321 -0
  216. package/src/v2/components/ui/presence.ts +54 -0
  217. package/src/v2/components/ui/use-id.ts +16 -0
  218. package/src/v2/hooks/use-default-render-tool.ts +123 -62
  219. package/src/v2/hooks/use-render-tool.ts +8 -0
  220. package/src/v2/index.ts +3 -0
  221. package/src/v2/lib/__tests__/message-feedback.test.ts +152 -0
  222. package/src/v2/lib/message-feedback.ts +120 -0
  223. package/src/v2/providers/CopilotChatConfigurationProvider.vue +29 -2
  224. package/src/v2/providers/CopilotKitProvider.types.ts +57 -0
  225. package/src/v2/providers/CopilotKitProvider.vue +125 -3
  226. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.openThread.test.ts +69 -0
  227. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +534 -0
  228. package/src/v2/providers/index.ts +4 -1
  229. package/src/v2/providers/keys.ts +12 -0
  230. package/src/v2/providers/types.ts +11 -0
  231. package/src/v2/styles/CopilotKitStyles.ts +95 -0
  232. package/src/v2/styles/__tests__/CopilotKitStyles.client.test.ts +149 -0
  233. package/src/v2/styles/__tests__/CopilotKitStyles.ssr.test.ts +71 -0
  234. package/src/v2/types/defineToolCallRenderer.ts +4 -0
  235. package/src/v2/types/vue-tool-call-renderer.ts +7 -0
  236. package/vite.config.ts +2 -1
  237. package/dist/styles.css +0 -2
  238. package/scripts/scope-preflight.mjs +0 -100
  239. package/src/styles/globals.css +0 -233
@@ -0,0 +1,321 @@
1
+ import { onBeforeUnmount, ref, watch } from "vue";
2
+ import type { CSSProperties, Ref } from "vue";
3
+
4
+ /**
5
+ * Positions floating UI (tooltips, popovers, menus) next to an element, the
6
+ * way React's Radix primitives do: fixed to the viewport, flipped to the other
7
+ * side when it does not fit, and shifted along its side to stay on screen.
8
+ * Floating content sits in a wrapper that carries the position, so the
9
+ * content's own enter and exit animations can use `transform`.
10
+ * @internal
11
+ */
12
+
13
+ export type FloatingSide = "top" | "right" | "bottom" | "left";
14
+ export type FloatingAlign = "start" | "center" | "end";
15
+
16
+ export interface FloatingOptions {
17
+ side?: FloatingSide;
18
+ align?: FloatingAlign;
19
+ /** Distance from the reference element, in px. */
20
+ sideOffset?: number;
21
+ /** Shift along the side, in px, for `start` and `end`. */
22
+ alignOffset?: number;
23
+ /** Space kept between the content and the viewport edge, in px. */
24
+ collisionPadding?: number;
25
+ /** Size of an arrow drawn on the content, in px (added to the offset). */
26
+ arrowSize?: number;
27
+ }
28
+
29
+ export interface FloatingPlacement {
30
+ x: number;
31
+ y: number;
32
+ side: FloatingSide;
33
+ align: FloatingAlign;
34
+ /** Room left between the reference element and the viewport edge. */
35
+ availableWidth: number;
36
+ availableHeight: number;
37
+ /** The reference element's center on the content, for its arrow. */
38
+ arrowX?: number;
39
+ arrowY?: number;
40
+ transformOrigin: string;
41
+ }
42
+
43
+ const OPPOSITE: Record<FloatingSide, FloatingSide> = {
44
+ top: "bottom",
45
+ bottom: "top",
46
+ left: "right",
47
+ right: "left",
48
+ };
49
+
50
+ const clamp = (value: number, min: number, max: number) =>
51
+ Math.min(Math.max(value, min), Math.max(min, max));
52
+
53
+ /** Where content of `size` goes next to `reference` in a `viewport`. */
54
+ export function computeFloatingPlacement(
55
+ reference: Pick<
56
+ DOMRect,
57
+ "top" | "left" | "right" | "bottom" | "width" | "height"
58
+ >,
59
+ size: { width: number; height: number },
60
+ viewport: { width: number; height: number },
61
+ options: FloatingOptions = {},
62
+ ): FloatingPlacement {
63
+ const {
64
+ side: preferredSide = "bottom",
65
+ align = "center",
66
+ sideOffset = 0,
67
+ alignOffset = 0,
68
+ collisionPadding = 0,
69
+ arrowSize = 0,
70
+ } = options;
71
+ const offset = sideOffset + arrowSize;
72
+
73
+ const space = (side: FloatingSide) =>
74
+ ({
75
+ top: reference.top,
76
+ bottom: viewport.height - reference.bottom,
77
+ left: reference.left,
78
+ right: viewport.width - reference.right,
79
+ })[side] -
80
+ offset -
81
+ collisionPadding;
82
+ const needed = (side: FloatingSide) =>
83
+ side === "top" || side === "bottom" ? size.height : size.width;
84
+
85
+ let side = preferredSide;
86
+ const opposite = OPPOSITE[preferredSide];
87
+ if (
88
+ space(side) < needed(side) &&
89
+ space(opposite) >= needed(opposite) &&
90
+ space(opposite) > space(side)
91
+ ) {
92
+ side = opposite;
93
+ }
94
+
95
+ const vertical = side === "top" || side === "bottom";
96
+ let x: number;
97
+ let y: number;
98
+ if (vertical) {
99
+ y =
100
+ side === "top"
101
+ ? reference.top - size.height - offset
102
+ : reference.bottom + offset;
103
+ x =
104
+ align === "start"
105
+ ? reference.left + alignOffset
106
+ : align === "end"
107
+ ? reference.right - size.width - alignOffset
108
+ : reference.left + reference.width / 2 - size.width / 2;
109
+ x = clamp(
110
+ x,
111
+ collisionPadding,
112
+ viewport.width - size.width - collisionPadding,
113
+ );
114
+ } else {
115
+ x =
116
+ side === "left"
117
+ ? reference.left - size.width - offset
118
+ : reference.right + offset;
119
+ y =
120
+ align === "start"
121
+ ? reference.top + alignOffset
122
+ : align === "end"
123
+ ? reference.bottom - size.height - alignOffset
124
+ : reference.top + reference.height / 2 - size.height / 2;
125
+ y = clamp(
126
+ y,
127
+ collisionPadding,
128
+ viewport.height - size.height - collisionPadding,
129
+ );
130
+ }
131
+
132
+ const arrowX = vertical
133
+ ? clamp(reference.left + reference.width / 2 - x, 0, size.width)
134
+ : undefined;
135
+ const arrowY = vertical
136
+ ? undefined
137
+ : clamp(reference.top + reference.height / 2 - y, 0, size.height);
138
+ const transformOrigin = vertical
139
+ ? `${arrowX}px ${side === "top" ? `${size.height + arrowSize}px` : `${-arrowSize}px`}`
140
+ : `${side === "left" ? `${size.width + arrowSize}px` : `${-arrowSize}px`} ${arrowY}px`;
141
+
142
+ return {
143
+ x,
144
+ y,
145
+ side,
146
+ align,
147
+ availableWidth: vertical
148
+ ? viewport.width - 2 * collisionPadding
149
+ : Math.max(0, space(side)),
150
+ availableHeight: vertical
151
+ ? Math.max(0, space(side))
152
+ : viewport.height - 2 * collisionPadding,
153
+ arrowX,
154
+ arrowY,
155
+ transformOrigin,
156
+ };
157
+ }
158
+
159
+ /** Whether `element` is scrolled or clipped fully out of view. */
160
+ export function isElementClippedAway(element: Element): boolean {
161
+ const rect = element.getBoundingClientRect();
162
+ const view = element.ownerDocument.defaultView;
163
+ if (!view) return false;
164
+ let top = 0;
165
+ let left = 0;
166
+ let bottom = view.innerHeight;
167
+ let right = view.innerWidth;
168
+ for (
169
+ let parent = element.parentElement;
170
+ parent;
171
+ parent = parent.parentElement
172
+ ) {
173
+ const { overflowX, overflowY } = view.getComputedStyle(parent);
174
+ if (overflowX === "visible" && overflowY === "visible") continue;
175
+ const clip = parent.getBoundingClientRect();
176
+ top = Math.max(top, clip.top);
177
+ left = Math.max(left, clip.left);
178
+ bottom = Math.min(bottom, clip.bottom);
179
+ right = Math.min(right, clip.right);
180
+ }
181
+ // Strict, so an element that only touches the edge still counts as shown.
182
+ return (
183
+ rect.bottom < top ||
184
+ rect.top > bottom ||
185
+ rect.right < left ||
186
+ rect.left > right
187
+ );
188
+ }
189
+
190
+ /**
191
+ * Keeps `floating` placed next to `reference` while `open`: on scroll,
192
+ * resize, and size changes of either element.
193
+ */
194
+ export function useFloating(
195
+ reference: Ref<HTMLElement | null | undefined>,
196
+ floating: Ref<HTMLElement | null | undefined>,
197
+ open: Ref<boolean>,
198
+ options: () => FloatingOptions & { hideWhenDetached?: boolean },
199
+ ) {
200
+ const placement = ref<FloatingPlacement | null>(null);
201
+ const referenceHidden = ref(false);
202
+ let cleanup: (() => void) | undefined;
203
+
204
+ const update = () => {
205
+ const referenceElement = reference.value;
206
+ const floatingElement = floating.value;
207
+ if (!referenceElement || !floatingElement) return;
208
+ const view = floatingElement.ownerDocument.defaultView;
209
+ if (!view) return;
210
+ const opts = options();
211
+ placement.value = computeFloatingPlacement(
212
+ referenceElement.getBoundingClientRect(),
213
+ {
214
+ width: floatingElement.offsetWidth,
215
+ height: floatingElement.offsetHeight,
216
+ },
217
+ { width: view.innerWidth, height: view.innerHeight },
218
+ opts,
219
+ );
220
+ referenceHidden.value =
221
+ !!opts.hideWhenDetached &&
222
+ (!referenceElement.isConnected || isElementClippedAway(referenceElement));
223
+ };
224
+
225
+ const stop = () => {
226
+ cleanup?.();
227
+ cleanup = undefined;
228
+ };
229
+
230
+ watch(
231
+ [open, reference, floating],
232
+ ([isOpen, referenceElement, floatingElement]) => {
233
+ stop();
234
+ if (!isOpen || !referenceElement || !floatingElement) {
235
+ if (!isOpen) placement.value = null;
236
+ return;
237
+ }
238
+ update();
239
+ const view = floatingElement.ownerDocument.defaultView;
240
+ if (!view) return;
241
+ let frame = 0;
242
+ const schedule = () => {
243
+ if (frame) return;
244
+ frame = view.requestAnimationFrame(() => {
245
+ frame = 0;
246
+ update();
247
+ });
248
+ };
249
+ view.addEventListener("scroll", schedule, {
250
+ capture: true,
251
+ passive: true,
252
+ });
253
+ view.addEventListener("resize", schedule);
254
+ const observer =
255
+ typeof ResizeObserver === "undefined"
256
+ ? undefined
257
+ : new ResizeObserver(schedule);
258
+ observer?.observe(referenceElement);
259
+ observer?.observe(floatingElement);
260
+ cleanup = () => {
261
+ if (frame) view.cancelAnimationFrame(frame);
262
+ view.removeEventListener("scroll", schedule, { capture: true });
263
+ view.removeEventListener("resize", schedule);
264
+ observer?.disconnect();
265
+ };
266
+ },
267
+ { flush: "post", immediate: true },
268
+ );
269
+
270
+ onBeforeUnmount(stop);
271
+
272
+ /** Style for the wrapper around the floating content. */
273
+ const wrapperStyle = (zIndex?: string | number): CSSProperties => {
274
+ const current = placement.value;
275
+ return {
276
+ position: "fixed",
277
+ left: "0px",
278
+ top: "0px",
279
+ minWidth: "max-content",
280
+ zIndex: zIndex ?? 50,
281
+ // Off screen until measured, as Radix does.
282
+ transform: current
283
+ ? `translate(${Math.round(current.x)}px, ${Math.round(current.y)}px)`
284
+ : "translate(0, -200%)",
285
+ visibility: referenceHidden.value ? "hidden" : undefined,
286
+ pointerEvents: referenceHidden.value ? "none" : undefined,
287
+ };
288
+ };
289
+
290
+ return { placement, referenceHidden, update, wrapperStyle };
291
+ }
292
+
293
+ /**
294
+ * Style for an arrow wrapper on floating content, as Radix places it: on the
295
+ * edge facing the reference element, centered on it.
296
+ */
297
+ export function arrowWrapperStyle(
298
+ placement: FloatingPlacement | null,
299
+ ): CSSProperties {
300
+ if (!placement) return { position: "absolute", visibility: "hidden" };
301
+ const { side, arrowX, arrowY } = placement;
302
+ return {
303
+ position: "absolute",
304
+ left: arrowX !== undefined ? `${arrowX}px` : undefined,
305
+ top: arrowY !== undefined ? `${arrowY}px` : undefined,
306
+ [OPPOSITE[side]]: "0px",
307
+ transformOrigin: {
308
+ top: "",
309
+ right: "0 0",
310
+ bottom: "center 0",
311
+ left: "100% 0",
312
+ }[side],
313
+ transform: {
314
+ top: "translateY(100%)",
315
+ right: "translateY(50%) rotate(90deg) translateX(-50%)",
316
+ bottom: "rotate(180deg)",
317
+ left: "translateY(50%) rotate(-90deg) translateX(50%)",
318
+ }[side],
319
+ marginLeft: arrowX !== undefined ? "-5px" : undefined,
320
+ };
321
+ }
@@ -0,0 +1,54 @@
1
+ import { nextTick, onBeforeUnmount, ref, watch } from "vue";
2
+ import type { Ref } from "vue";
3
+
4
+ /**
5
+ * Keeps closing content mounted until its exit animation ends, like Radix's
6
+ * Presence: the content gets `data-state="closed"`, and its CSS animation
7
+ * (for example `cpk-tooltip-exit`) plays before it unmounts.
8
+ * @internal
9
+ */
10
+ export function usePresence(
11
+ open: Ref<boolean>,
12
+ element: Ref<HTMLElement | null | undefined>,
13
+ ) {
14
+ const present = ref(open.value);
15
+ let finish: (() => void) | undefined;
16
+
17
+ const stopWaiting = () => {
18
+ finish?.();
19
+ finish = undefined;
20
+ };
21
+
22
+ watch(open, async (isOpen) => {
23
+ stopWaiting();
24
+ if (isOpen) {
25
+ present.value = true;
26
+ return;
27
+ }
28
+ // Let `data-state="closed"` reach the DOM, so its animation starts.
29
+ await nextTick();
30
+ if (open.value) return;
31
+ const node = element.value;
32
+ const view = node?.ownerDocument.defaultView;
33
+ const animationName = node && view?.getComputedStyle(node).animationName;
34
+ if (!node || !view || !animationName || animationName === "none") {
35
+ present.value = false;
36
+ return;
37
+ }
38
+ const done = () => {
39
+ stopWaiting();
40
+ if (!open.value) present.value = false;
41
+ };
42
+ // A fallback, in case the animation is cancelled without an end event.
43
+ const timer = view.setTimeout(done, 400);
44
+ node.addEventListener("animationend", done, { once: true });
45
+ finish = () => {
46
+ view.clearTimeout(timer);
47
+ node.removeEventListener("animationend", done);
48
+ };
49
+ });
50
+
51
+ onBeforeUnmount(stopWaiting);
52
+
53
+ return present;
54
+ }
@@ -0,0 +1,16 @@
1
+ import * as vue from "vue";
2
+
3
+ let counter = 0;
4
+
5
+ /**
6
+ * A unique id for ARIA references. Uses Vue's SSR-safe `useId()` on Vue 3.5+;
7
+ * the peer range starts at 3.3, which has none, so older Vue gets a counter.
8
+ * Read through the namespace, so a Vue without the export does not fail to
9
+ * import this module.
10
+ * @internal
11
+ */
12
+ export function useCopilotId(prefix = "cpk"): string {
13
+ const vueUseId = (vue as { useId?: () => string }).useId;
14
+ const id = vueUseId?.();
15
+ return id ? `${prefix}-${id}` : `${prefix}-${++counter}`;
16
+ }
@@ -1,8 +1,10 @@
1
- import { defineComponent, h, ref } from "vue";
1
+ import { defineComponent, h, inject, ref } from "vue";
2
2
  import type { WatchSource } from "vue";
3
3
  import type { Component, VNodeChild } from "vue";
4
4
  import { ToolCallStatus } from "@copilotkit/core";
5
5
  import { useRenderTool } from "./use-render-tool";
6
+ import { ToolCallGroupContextKey } from "../components/chat/tool-call-display-context";
7
+ import { ToolCallRow } from "../components/chat/tool-call-group-parts";
6
8
 
7
9
  type DefaultRenderProps = {
8
10
  name: string;
@@ -135,8 +137,34 @@ const DefaultToolCallRenderer = defineComponent({
135
137
  },
136
138
  setup(props) {
137
139
  const isExpanded = ref(false);
140
+ const groupContext = inject(ToolCallGroupContextKey, null);
138
141
 
139
142
  return () => {
143
+ const group = groupContext?.value;
144
+ // Inside a group of calls, a row that fits the group instead of a card.
145
+ if (group) {
146
+ return h(ToolCallRow, {
147
+ "data-testid": "copilot-tool-render",
148
+ "data-tool-name": props.name,
149
+ "data-tool-call-id": props.toolCallId,
150
+ "data-status": props.status,
151
+ "data-args": safeStringifyForAttr(props.parameters),
152
+ "data-result": safeStringifyForAttr(props.result),
153
+ name: props.name,
154
+ parameters: props.parameters,
155
+ result: props.result,
156
+ status:
157
+ props.status === "complete"
158
+ ? group.failedIds.has(props.toolCallId)
159
+ ? "failed"
160
+ : "done"
161
+ : group.active
162
+ ? "running"
163
+ : "stopped",
164
+ appearance: group.appearance,
165
+ });
166
+ }
167
+
140
168
  const isActive =
141
169
  props.status === "inProgress" || props.status === "executing";
142
170
  const isComplete = props.status === "complete";
@@ -145,7 +173,10 @@ const DefaultToolCallRenderer = defineComponent({
145
173
  : isComplete
146
174
  ? "Done"
147
175
  : props.status;
176
+ const tone = isActive ? "running" : isComplete ? "done" : "other";
148
177
 
178
+ // Markup and classes match React's DefaultToolCallRenderer; styles live
179
+ // in @copilotkit/core (default-tool-call.css).
149
180
  return h(
150
181
  "div",
151
182
  {
@@ -155,77 +186,99 @@ const DefaultToolCallRenderer = defineComponent({
155
186
  "data-status": props.status,
156
187
  "data-args": safeStringifyForAttr(props.parameters),
157
188
  "data-result": safeStringifyForAttr(props.result),
158
- style: { marginTop: "8px", paddingBottom: "8px" },
189
+ class: "cpk-default-tool-call",
159
190
  },
160
191
  [
161
- h(
162
- "div",
163
- {
164
- style: {
165
- borderRadius: "12px",
166
- border: "1px solid #e4e4e7",
167
- backgroundColor: "#fafafa",
168
- padding: "14px 16px",
169
- },
170
- },
171
- [
172
- h(
173
- "button",
174
- {
175
- type: "button",
176
- "aria-expanded": String(isExpanded.value),
177
- onClick: () => {
178
- isExpanded.value = !isExpanded.value;
179
- },
180
- style: {
181
- width: "100%",
182
- display: "flex",
183
- alignItems: "center",
184
- justifyContent: "space-between",
185
- gap: "10px",
186
- cursor: "pointer",
187
- border: "none",
188
- padding: 0,
189
- margin: 0,
190
- background: "transparent",
191
- textAlign: "left",
192
- },
192
+ h("div", { class: "cpk-default-tool-call-card" }, [
193
+ h(
194
+ "button",
195
+ {
196
+ type: "button",
197
+ "aria-expanded": String(isExpanded.value),
198
+ onClick: () => {
199
+ isExpanded.value = !isExpanded.value;
193
200
  },
194
- [
201
+ class: "cpk-default-tool-call-header",
202
+ style: { font: "inherit" },
203
+ },
204
+ [
205
+ h("div", { class: "cpk-default-tool-call-title" }, [
195
206
  h(
196
- "span",
207
+ "svg",
197
208
  {
198
- "data-testid": "copilot-tool-render-name",
199
- style: { fontWeight: "600" },
209
+ class: [
210
+ "cpk-default-tool-call-chevron",
211
+ isExpanded.value &&
212
+ "cpk-default-tool-call-chevron-open",
213
+ ],
214
+ fill: "none",
215
+ viewBox: "0 0 24 24",
216
+ "stroke-width": 2,
217
+ stroke: "currentColor",
200
218
  },
201
- props.name,
219
+ [
220
+ h("path", {
221
+ "stroke-linecap": "round",
222
+ "stroke-linejoin": "round",
223
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5",
224
+ }),
225
+ ],
202
226
  ),
227
+ h("span", {
228
+ class: `cpk-default-tool-call-dot cpk-default-tool-call-dot-${tone}`,
229
+ }),
203
230
  h(
204
231
  "span",
205
- { "data-testid": "copilot-tool-render-status" },
206
- statusLabel,
232
+ {
233
+ "data-testid": "copilot-tool-render-name",
234
+ class: "cpk-default-tool-call-name",
235
+ },
236
+ props.name,
207
237
  ),
208
- ],
209
- ),
210
- isExpanded.value
211
- ? h("div", { style: { marginTop: "12px" } }, [
212
- h("div", "Arguments"),
213
- h("pre", safeStringifyForPre(props.parameters ?? {})),
214
- props.result !== undefined
215
- ? h("div", [
216
- h("div", "Result"),
217
- h(
218
- "pre",
219
- typeof props.result === "string"
220
- ? props.result
221
- : safeStringifyForPre(props.result),
222
- ),
223
- ])
224
- : null,
225
- ])
226
- : null,
227
- ],
228
- ),
238
+ ]),
239
+ h(
240
+ "span",
241
+ {
242
+ "data-testid": "copilot-tool-render-status",
243
+ class: `cpk-default-tool-call-badge cpk-default-tool-call-badge-${tone}`,
244
+ },
245
+ statusLabel,
246
+ ),
247
+ ],
248
+ ),
249
+ isExpanded.value
250
+ ? h("div", { class: "cpk-default-tool-call-details" }, [
251
+ h("div", [
252
+ h(
253
+ "div",
254
+ { class: "cpk-default-tool-call-label" },
255
+ "Arguments",
256
+ ),
257
+ h(
258
+ "pre",
259
+ { class: "cpk-default-tool-call-code" },
260
+ safeStringifyForPre(props.parameters ?? {}),
261
+ ),
262
+ ]),
263
+ props.result !== undefined
264
+ ? h("div", [
265
+ h(
266
+ "div",
267
+ { class: "cpk-default-tool-call-label" },
268
+ "Result",
269
+ ),
270
+ h(
271
+ "pre",
272
+ { class: "cpk-default-tool-call-code" },
273
+ typeof props.result === "string"
274
+ ? props.result
275
+ : safeStringifyForPre(props.result),
276
+ ),
277
+ ])
278
+ : null,
279
+ ])
280
+ : null,
281
+ ]),
229
282
  ],
230
283
  );
231
284
  };
@@ -259,6 +312,12 @@ export function useDefaultRenderTool(
259
312
  render?:
260
313
  | ((props: DefaultRenderProps) => VNodeChild)
261
314
  | Component<DefaultRenderProps>;
315
+ /**
316
+ * Let the chat group calls your `render` draws, like it groups the
317
+ * built-in card's (see `toolCallDisplay` on `CopilotChatMessageView`).
318
+ * Off by default for a custom `render`, since it may draw nothing.
319
+ */
320
+ group?: boolean;
262
321
  },
263
322
  deps?: WatchSource<unknown>[],
264
323
  ): void {
@@ -312,6 +371,8 @@ export function useDefaultRenderTool(
312
371
  {
313
372
  name: "*",
314
373
  render: registeredRender,
374
+ // The built-in card draws a row inside a group; a custom render opts in.
375
+ group: userRender ? config?.group === true : true,
315
376
  },
316
377
  deps,
317
378
  );
@@ -46,6 +46,7 @@ type RenderToolConfig<S extends StandardSchemaV1> = {
46
46
  | ((props: RenderToolProps<S>) => VNodeChild)
47
47
  | Component<RenderToolProps<S>>;
48
48
  agentId?: string;
49
+ group?: boolean;
49
50
  };
50
51
 
51
52
  export function useRenderTool(
@@ -53,6 +54,8 @@ export function useRenderTool(
53
54
  name: "*";
54
55
  render: ((props: any) => VNodeChild) | Component<any>;
55
56
  agentId?: string;
57
+ /** Let the chat group the calls this renderer draws (see `toolCallDisplay`). */
58
+ group?: boolean;
56
59
  },
57
60
  deps?: WatchSource<unknown>[],
58
61
  ): void;
@@ -65,6 +68,8 @@ export function useRenderTool<S extends StandardSchemaV1>(
65
68
  | ((props: RenderToolProps<S>) => VNodeChild)
66
69
  | Component<RenderToolProps<S>>;
67
70
  agentId?: string;
71
+ /** Let the chat group the calls this renderer draws (see `toolCallDisplay`). */
72
+ group?: boolean;
68
73
  },
69
74
  deps?: WatchSource<unknown>[],
70
75
  ): void;
@@ -81,6 +86,7 @@ export function useRenderTool<S extends StandardSchemaV1>(
81
86
  () => copilotkit.value,
82
87
  () => config.name,
83
88
  () => config.agentId,
89
+ () => config.group,
84
90
  () => extraDeps.length,
85
91
  ...extraDeps,
86
92
  ],
@@ -104,6 +110,7 @@ export function useRenderTool<S extends StandardSchemaV1>(
104
110
  parameters: props.args,
105
111
  } as RenderToolProps<S>),
106
112
  ...(config.agentId ? { agentId: config.agentId } : {}),
113
+ ...(config.group ? { group: true } : {}),
107
114
  })
108
115
  : defineToolCallRenderer({
109
116
  name: config.name,
@@ -116,6 +123,7 @@ export function useRenderTool<S extends StandardSchemaV1>(
116
123
  parameters: props.args,
117
124
  } as RenderToolProps<S>),
118
125
  ...(config.agentId ? { agentId: config.agentId } : {}),
126
+ ...(config.group ? { group: true } : {}),
119
127
  });
120
128
 
121
129
  copilotkit.value.addHookRenderToolCall(
package/src/v2/index.ts CHANGED
@@ -16,6 +16,9 @@ export * from "./components";
16
16
  export { vueBasicCatalog } from "./components/a2ui/catalog";
17
17
  export * from "./hooks";
18
18
  export * from "./providers";
19
+ export { CopilotKitStyles } from "./styles/CopilotKitStyles";
20
+ export type { A2UIMessageRendererOptions } from "./components/A2UIMessageRenderer";
21
+ export type { A2UIRecoveryRendererOptions } from "./components/a2ui/A2UIRecoveryStates";
19
22
  export * from "./types";
20
23
  export * from "./lib/vue-core";
21
24
  export * from "./lib/processPartialHtml";