@copilotkit/vue 2.0.0-rc.0 → 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
@@ -7,6 +7,8 @@ import {
7
7
  } from "@copilotkit/shared";
8
8
  import type { Attachment } from "@copilotkit/shared";
9
9
  import type { CopilotChatAttachmentQueueProps } from "./types";
10
+ import { IconPlay } from "../icons";
11
+ import CopilotChatLightbox from "./CopilotChatLightbox.vue";
10
12
  import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
11
13
  import { CopilotChatDefaultLabels } from "../../providers/types";
12
14
 
@@ -25,7 +27,16 @@ const labels = computed(() => config.value?.labels ?? CopilotChatDefaultLabels);
25
27
  const activeLightboxAttachment = ref<Attachment | null>(null);
26
28
  const documentBlobUrl = ref<string | null>(null);
27
29
 
28
- const isLightboxOpen = computed(() => activeLightboxAttachment.value !== null);
30
+ const lightboxLabel = computed(() => {
31
+ const attachment = activeLightboxAttachment.value;
32
+ if (!attachment) return undefined;
33
+ if (attachment.filename) return attachment.filename;
34
+ return attachment.type === "image"
35
+ ? "Image attachment"
36
+ : attachment.type === "video"
37
+ ? "Video attachment"
38
+ : "Document";
39
+ });
29
40
  const isPreviewableDocument = computed(() => {
30
41
  const attachment = activeLightboxAttachment.value;
31
42
  if (!attachment || attachment.type !== "document") return false;
@@ -78,20 +89,6 @@ watch(
78
89
  { immediate: true },
79
90
  );
80
91
 
81
- watch(isLightboxOpen, (open, _previous, onCleanup) => {
82
- if (!open || typeof document === "undefined") return;
83
-
84
- const handleKeydown = (event: KeyboardEvent) => {
85
- if (event.key === "Escape") {
86
- closeLightbox();
87
- }
88
- };
89
- document.addEventListener("keydown", handleKeydown);
90
- onCleanup(() => {
91
- document.removeEventListener("keydown", handleKeydown);
92
- });
93
- });
94
-
95
92
  onBeforeUnmount(() => {
96
93
  if (documentBlobUrl.value) {
97
94
  URL.revokeObjectURL(documentBlobUrl.value);
@@ -120,12 +117,12 @@ function closeLightbox() {
120
117
 
121
118
  function cardClassName(attachment: Attachment) {
122
119
  if (attachment.type === "image" || attachment.type === "video") {
123
- return "cpk:w-[72px] cpk:h-[72px]";
120
+ return "cpk-attachment-queue-item-media";
124
121
  }
125
122
  if (attachment.type === "audio") {
126
- return "cpk:min-w-[200px] cpk:max-w-[280px] cpk:flex-col cpk:p-1 cpk:pr-8";
123
+ return "cpk-attachment-queue-item-audio";
127
124
  }
128
- return "cpk:p-2 cpk:px-3 cpk:pr-8 cpk:max-w-[240px]";
125
+ return "cpk-attachment-queue-item-document";
129
126
  }
130
127
 
131
128
  function isPdf(mimeType: string | undefined): boolean {
@@ -160,92 +157,87 @@ function createBlobUrl(
160
157
  <template>
161
158
  <div
162
159
  v-if="props.attachments.length > 0"
163
- class="cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2"
160
+ class="cpk-attachment-queue"
164
161
  :class="props.className"
165
162
  data-testid="copilot-chat-attachment-queue"
166
163
  >
167
164
  <div
168
165
  v-for="attachment in props.attachments"
169
166
  :key="attachment.id"
170
- class="cpk:relative cpk:inline-flex cpk:rounded-lg cpk:overflow-hidden cpk:border cpk:border-border"
167
+ class="cpk-attachment-queue-item"
171
168
  :class="cardClassName(attachment)"
172
169
  data-testid="copilot-chat-attachment-item"
173
170
  :data-card-type="attachment.type"
174
171
  >
175
172
  <div
176
173
  v-if="attachment.status === 'uploading'"
177
- class="cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10"
174
+ class="cpk-attachment-queue-uploading-overlay"
178
175
  data-testid="copilot-chat-attachment-uploading-overlay"
179
176
  >
180
- <div
181
- class="cpk:w-5 cpk:h-5 cpk:border-2 cpk:border-white cpk:border-t-transparent cpk:rounded-full cpk:animate-spin"
182
- />
177
+ <div class="cpk-attachment-queue-uploading-spinner" />
183
178
  </div>
184
179
 
185
180
  <template v-if="attachment.status === 'ready'">
186
181
  <button
187
182
  v-if="attachment.type === 'image'"
188
183
  type="button"
189
- class="cpk:w-full cpk:h-full cpk:border-none cpk:bg-transparent cpk:p-0 cpk:cursor-pointer"
184
+ class="cpk-attachment-queue-preview-button"
190
185
  data-testid="copilot-chat-attachment-image-button"
191
186
  @click="openLightbox(attachment)"
192
187
  >
193
188
  <img
194
189
  :src="getSourceUrl(attachment.source)"
195
190
  :alt="attachment.filename || 'Image attachment'"
196
- class="cpk:w-full cpk:h-full cpk:object-cover"
191
+ class="cpk-attachment-queue-image"
197
192
  data-testid="copilot-chat-attachment-image-thumbnail"
198
193
  />
199
194
  </button>
200
195
 
201
- <div
202
- v-else-if="attachment.type === 'video'"
203
- class="cpk:w-full cpk:h-full cpk:relative"
204
- >
205
- <img
206
- v-if="attachment.thumbnail"
207
- :src="attachment.thumbnail"
208
- :alt="attachment.filename || 'Video thumbnail'"
209
- class="cpk:w-full cpk:h-full cpk:object-cover"
210
- data-testid="copilot-chat-attachment-video-thumbnail"
211
- />
212
- <video
213
- v-else
214
- :src="getSourceUrl(attachment.source)"
215
- preload="metadata"
216
- muted
217
- class="cpk:w-full cpk:h-full cpk:object-cover"
218
- data-testid="copilot-chat-attachment-video-fallback"
219
- />
196
+ <template v-else-if="attachment.type === 'video'">
197
+ <div class="cpk-attachment-queue-video">
198
+ <img
199
+ v-if="attachment.thumbnail"
200
+ :src="attachment.thumbnail"
201
+ :alt="attachment.filename || 'Video thumbnail'"
202
+ class="cpk-attachment-queue-video-thumbnail"
203
+ data-testid="copilot-chat-attachment-video-thumbnail"
204
+ />
205
+ <video
206
+ v-else
207
+ :src="getSourceUrl(attachment.source)"
208
+ preload="metadata"
209
+ muted
210
+ class="cpk-attachment-queue-video-thumbnail"
211
+ data-testid="copilot-chat-attachment-video-fallback"
212
+ />
213
+ </div>
220
214
  <button
221
215
  type="button"
222
- class="cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:z-10 cpk:cursor-pointer cpk:bg-black/20 cpk:border-none cpk:p-0"
216
+ class="cpk-attachment-queue-video-play-button"
223
217
  :aria-label="labels.attachmentPlayVideoButtonLabel"
224
218
  data-testid="copilot-chat-attachment-video-play"
225
219
  @click="openLightbox(attachment)"
226
220
  >
227
- <div
228
- class="cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center cpk:text-white cpk:text-xs cpk:font-semibold"
229
- >
230
- ▶
221
+ <div class="cpk-attachment-queue-video-play-badge">
222
+ <IconPlay class="cpk-attachment-queue-video-play-icon" />
231
223
  </div>
232
224
  </button>
233
- </div>
225
+ </template>
234
226
 
235
227
  <div
236
228
  v-else-if="attachment.type === 'audio'"
237
- class="cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full"
229
+ class="cpk-attachment-queue-audio"
238
230
  >
239
231
  <audio
240
232
  :src="getSourceUrl(attachment.source)"
241
233
  controls
242
234
  preload="metadata"
243
- class="cpk:w-full cpk:h-8"
235
+ class="cpk-attachment-queue-audio-player"
244
236
  data-testid="copilot-chat-attachment-audio-player"
245
237
  />
246
238
  <span
247
239
  v-if="attachment.filename"
248
- class="cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap"
240
+ class="cpk-attachment-queue-audio-filename"
249
241
  >
250
242
  {{ attachment.filename }}
251
243
  </span>
@@ -254,13 +246,12 @@ function createBlobUrl(
254
246
  <button
255
247
  v-else
256
248
  type="button"
257
- class="cpk:flex cpk:items-center cpk:gap-2 cpk:w-full cpk:border-none cpk:bg-transparent cpk:p-0 cpk:text-left"
258
- :class="
259
- isPdf(attachment.source.mimeType) ||
260
- isText(attachment.source.mimeType)
261
- ? 'cpk:cursor-pointer'
262
- : 'cpk:cursor-default'
263
- "
249
+ class="cpk-attachment-queue-preview-button cpk-attachment-queue-document"
250
+ :class="{
251
+ 'cpk-attachment-queue-document-previewable':
252
+ isPdf(attachment.source.mimeType) ||
253
+ isText(attachment.source.mimeType),
254
+ }"
264
255
  data-testid="copilot-chat-attachment-document-button"
265
256
  @click="
266
257
  (isPdf(attachment.source.mimeType) ||
@@ -268,21 +259,19 @@ function createBlobUrl(
268
259
  openLightbox(attachment)
269
260
  "
270
261
  >
271
- <div
272
- class="cpk:w-8 cpk:h-8 cpk:rounded-md cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-[10px] cpk:font-semibold cpk:shrink-0"
273
- >
262
+ <div class="cpk-attachment-queue-document-icon">
274
263
  {{ getDocumentIcon(attachment.source.mimeType ?? "") }}
275
264
  </div>
276
- <div class="cpk:flex cpk:flex-col cpk:min-w-0">
265
+ <div class="cpk-attachment-queue-document-info">
277
266
  <span
278
- class="cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight"
267
+ class="cpk-attachment-queue-document-name"
279
268
  data-testid="copilot-chat-attachment-document-filename"
280
269
  >
281
270
  {{ attachment.filename || "Document" }}
282
271
  </span>
283
272
  <span
284
273
  v-if="attachment.size != null"
285
- class="cpk:text-[11px] cpk:text-muted-foreground"
274
+ class="cpk-attachment-queue-document-size"
286
275
  >
287
276
  {{ formatFileSize(attachment.size) }}
288
277
  </span>
@@ -292,13 +281,13 @@ function createBlobUrl(
292
281
 
293
282
  <div
294
283
  v-else
295
- class="cpk:w-full cpk:h-full cpk:bg-muted-foreground/20"
284
+ class="cpk-attachment-queue-placeholder"
296
285
  data-testid="copilot-chat-attachment-placeholder"
297
286
  />
298
287
 
299
288
  <button
300
289
  type="button"
301
- class="cpk:absolute cpk:bg-black/60 cpk:text-white cpk:border-none cpk:rounded-full cpk:w-5 cpk:h-5 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:text-[10px] cpk:z-20 cpk:top-1 cpk:right-1"
290
+ class="cpk-attachment-queue-remove-button"
302
291
  :aria-label="labels.attachmentRemoveButtonLabel"
303
292
  @click="emit('remove-attachment', attachment.id)"
304
293
  >
@@ -307,110 +296,94 @@ function createBlobUrl(
307
296
  </div>
308
297
  </div>
309
298
 
310
- <Teleport to="body">
311
- <div
312
- v-if="activeLightboxAttachment"
313
- class="cpk:fixed cpk:inset-0 cpk:z-[9999] cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/80"
314
- data-testid="copilot-chat-attachment-lightbox"
315
- @click="closeLightbox"
316
- >
317
- <button
318
- type="button"
319
- class="cpk:absolute cpk:top-4 cpk:right-4 cpk:text-white cpk:bg-white/10 cpk:hover:bg-white/20 cpk:rounded-full cpk:w-10 cpk:h-10 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:border-none"
320
- :aria-label="labels.attachmentPreviewCloseButtonLabel"
321
- @click.stop="closeLightbox"
322
- >
323
- ✕
324
- </button>
299
+ <CopilotChatLightbox
300
+ v-if="activeLightboxAttachment"
301
+ :label="lightboxLabel"
302
+ @close="closeLightbox"
303
+ >
304
+ <img
305
+ v-if="activeLightboxAttachment.type === 'image'"
306
+ :src="getSourceUrl(activeLightboxAttachment.source)"
307
+ :alt="activeLightboxAttachment.filename || 'Image attachment'"
308
+ class="cpk-attachment-queue-lightbox-image"
309
+ data-testid="copilot-chat-attachment-lightbox-image"
310
+ />
325
311
 
326
- <div data-testid="copilot-chat-attachment-lightbox-content" @click.stop>
327
- <img
328
- v-if="activeLightboxAttachment.type === 'image'"
329
- :src="getSourceUrl(activeLightboxAttachment.source)"
330
- :alt="activeLightboxAttachment.filename || 'Image attachment'"
331
- class="cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
332
- data-testid="copilot-chat-attachment-lightbox-image"
333
- />
312
+ <video
313
+ v-else-if="activeLightboxAttachment.type === 'video'"
314
+ :src="getSourceUrl(activeLightboxAttachment.source)"
315
+ controls
316
+ autoplay
317
+ class="cpk-attachment-queue-lightbox-video"
318
+ data-testid="copilot-chat-attachment-lightbox-video"
319
+ />
334
320
 
335
- <video
336
- v-else-if="activeLightboxAttachment.type === 'video'"
337
- :src="getSourceUrl(activeLightboxAttachment.source)"
338
- controls
339
- autoplay
340
- class="cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
341
- data-testid="copilot-chat-attachment-lightbox-video"
342
- />
321
+ <template v-else-if="activeLightboxAttachment.type === 'document'">
322
+ <iframe
323
+ v-if="
324
+ isPreviewableDocument &&
325
+ isPdf(activeLightboxAttachment.source.mimeType) &&
326
+ lightboxDocumentSource
327
+ "
328
+ :src="lightboxDocumentSource"
329
+ :title="activeLightboxAttachment.filename || 'PDF preview'"
330
+ class="cpk-attachment-queue-pdf-preview"
331
+ data-testid="copilot-chat-attachment-lightbox-document-iframe"
332
+ />
343
333
 
344
- <template v-else-if="activeLightboxAttachment.type === 'document'">
345
- <iframe
346
- v-if="
347
- isPreviewableDocument &&
348
- isPdf(activeLightboxAttachment.source.mimeType) &&
349
- lightboxDocumentSource
350
- "
351
- :src="lightboxDocumentSource"
352
- :title="activeLightboxAttachment.filename || 'PDF preview'"
353
- class="cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
354
- data-testid="copilot-chat-attachment-lightbox-document-iframe"
355
- />
334
+ <div
335
+ v-else-if="
336
+ isPreviewableDocument &&
337
+ isText(activeLightboxAttachment.source.mimeType)
338
+ "
339
+ class="cpk-attachment-queue-text-preview"
340
+ >
341
+ <div
342
+ v-if="activeLightboxAttachment.filename"
343
+ class="cpk-attachment-queue-text-preview-filename"
344
+ >
345
+ {{ activeLightboxAttachment.filename }}
346
+ </div>
347
+ <pre
348
+ v-if="decodedTextPreview !== null"
349
+ class="cpk-attachment-queue-text-preview-content"
350
+ data-testid="copilot-chat-attachment-lightbox-document-text"
351
+ >{{ decodedTextPreview }}</pre
352
+ >
353
+ <iframe
354
+ v-else-if="lightboxDocumentSource"
355
+ :src="lightboxDocumentSource"
356
+ :title="activeLightboxAttachment.filename || 'Text preview'"
357
+ class="cpk-attachment-queue-text-preview-frame"
358
+ data-testid="copilot-chat-attachment-lightbox-document-iframe"
359
+ />
360
+ </div>
356
361
 
357
- <div
358
- v-else-if="
359
- isPreviewableDocument &&
360
- isText(activeLightboxAttachment.source.mimeType)
361
- "
362
- class="cpk:w-[90vw] cpk:max-w-[800px] cpk:max-h-[90vh] cpk:overflow-auto cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900 cpk:p-6"
363
- >
364
- <pre
365
- v-if="decodedTextPreview !== null"
366
- class="cpk:text-sm cpk:whitespace-pre-wrap cpk:wrap-break-word cpk:text-gray-800 cpk:dark:text-gray-200 cpk:font-mono cpk:m-0"
367
- data-testid="copilot-chat-attachment-lightbox-document-text"
368
- >{{ decodedTextPreview }}</pre
369
- >
370
- <iframe
371
- v-else-if="lightboxDocumentSource"
372
- :src="lightboxDocumentSource"
373
- :title="activeLightboxAttachment.filename || 'Text preview'"
374
- class="cpk:w-full cpk:h-[80vh] cpk:border-none"
375
- data-testid="copilot-chat-attachment-lightbox-document-iframe"
376
- />
362
+ <div
363
+ v-else
364
+ class="cpk-attachment-queue-document-card"
365
+ data-testid="copilot-chat-attachment-lightbox-document-fallback"
366
+ >
367
+ <div class="cpk-attachment-queue-document-card-icon">
368
+ {{ getDocumentIcon(activeLightboxAttachment.source.mimeType ?? "") }}
369
+ </div>
370
+ <div class="cpk-attachment-queue-document-card-details">
371
+ <div class="cpk-attachment-queue-document-card-name">
372
+ {{ activeLightboxAttachment.filename || "Document" }}
377
373
  </div>
378
-
379
- <div
380
- v-else
381
- class="cpk:flex cpk:flex-col cpk:items-center cpk:gap-4 cpk:p-8 cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900"
382
- data-testid="copilot-chat-attachment-lightbox-document-fallback"
383
- >
384
- <div
385
- class="cpk:w-16 cpk:h-16 cpk:rounded-xl cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-xl cpk:font-bold"
386
- >
387
- {{
388
- getDocumentIcon(activeLightboxAttachment.source.mimeType ?? "")
389
- }}
390
- </div>
391
- <div class="cpk:text-center">
392
- <div
393
- class="cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200"
394
- >
395
- {{ activeLightboxAttachment.filename || "Document" }}
396
- </div>
397
- <div
398
- class="cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1"
399
- >
400
- {{ activeLightboxAttachment.source.mimeType || "Unknown type" }}
401
- {{
402
- activeLightboxAttachment.size != null
403
- ? ` · ${formatFileSize(activeLightboxAttachment.size)}`
404
- : ""
405
- }}
406
- </div>
407
- </div>
408
- <div class="cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500">
409
- No preview available for this file type
410
- </div>
374
+ <div class="cpk-attachment-queue-document-card-meta">
375
+ {{ activeLightboxAttachment.source.mimeType || "Unknown type" }}
376
+ {{
377
+ activeLightboxAttachment.size != null
378
+ ? ` · ${formatFileSize(activeLightboxAttachment.size)}`
379
+ : ""
380
+ }}
411
381
  </div>
412
- </template>
382
+ </div>
383
+ <div class="cpk-attachment-queue-document-card-note">
384
+ No preview available for this file type
385
+ </div>
413
386
  </div>
414
- </div>
415
- </Teleport>
387
+ </template>
388
+ </CopilotChatLightbox>
416
389
  </template>
@@ -2,6 +2,8 @@
2
2
  import { computed, ref } from "vue";
3
3
  import { getDocumentIcon, getSourceUrl } from "@copilotkit/shared";
4
4
  import type { CopilotChatAttachmentRendererProps } from "./types";
5
+ import CopilotChatLightbox from "./CopilotChatLightbox.vue";
6
+ import { useLightbox } from "./use-lightbox";
5
7
 
6
8
  const props = withDefaults(defineProps<CopilotChatAttachmentRendererProps>(), {
7
9
  filename: undefined,
@@ -13,21 +15,59 @@ const sourceUrl = computed(() => getSourceUrl(props.source));
13
15
  const documentLabel = computed(
14
16
  () => props.filename || props.source.mimeType || "Unknown type",
15
17
  );
18
+ const documentAriaLabel = computed(
19
+ () => props.filename || props.source.mimeType || "Document attachment",
20
+ );
21
+
22
+ const imageButton = ref<HTMLButtonElement | null>(null);
23
+ const {
24
+ thumbnailRef,
25
+ vtName,
26
+ open: lightboxOpen,
27
+ openLightbox,
28
+ closeLightbox,
29
+ } = useLightbox();
16
30
  </script>
17
31
 
18
32
  <template>
19
- <img
33
+ <!-- React opens the lightbox from the <img>; a button around it lets
34
+ keyboard users open it too. The button has no look of its own. -->
35
+ <button
20
36
  v-if="props.type === 'image' && !imageLoadFailed"
21
- :src="sourceUrl"
22
- alt="Image attachment"
23
- class="cpk:max-w-full cpk:h-auto cpk:rounded-lg"
24
- :class="props.className"
25
- data-testid="copilot-chat-attachment-renderer-image"
26
- @error="imageLoadFailed = true"
27
- />
37
+ ref="imageButton"
38
+ type="button"
39
+ class="cpk-attachment-renderer-image-button"
40
+ data-testid="copilot-chat-attachment-renderer-image-button"
41
+ @click="openLightbox"
42
+ >
43
+ <img
44
+ ref="thumbnailRef"
45
+ :src="sourceUrl"
46
+ alt="Image attachment"
47
+ class="cpk-attachment-renderer cpk-attachment-renderer-image"
48
+ :class="props.className"
49
+ data-testid="copilot-chat-attachment-renderer-image"
50
+ @error="imageLoadFailed = true"
51
+ />
52
+ <!-- Teleported to the body; inside the button only to keep one root. -->
53
+ <CopilotChatLightbox
54
+ v-if="lightboxOpen"
55
+ label="Image attachment"
56
+ :return-focus-to="imageButton"
57
+ @close="closeLightbox"
58
+ >
59
+ <img
60
+ :style="{ viewTransitionName: vtName }"
61
+ :src="sourceUrl"
62
+ alt="Image attachment"
63
+ class="cpk-attachment-renderer-lightbox-image"
64
+ data-testid="copilot-chat-attachment-renderer-lightbox-image"
65
+ />
66
+ </CopilotChatLightbox>
67
+ </button>
28
68
  <div
29
69
  v-else-if="props.type === 'image'"
30
- class="cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:rounded-lg cpk:bg-muted cpk:p-4 cpk:text-sm cpk:text-muted-foreground"
70
+ class="cpk-attachment-renderer cpk-attachment-renderer-image-error"
31
71
  :class="props.className"
32
72
  data-testid="copilot-chat-attachment-renderer-image-fallback"
33
73
  >
@@ -36,7 +76,7 @@ const documentLabel = computed(
36
76
 
37
77
  <div
38
78
  v-else-if="props.type === 'audio'"
39
- class="cpk:flex cpk:flex-col cpk:gap-1"
79
+ class="cpk-attachment-renderer cpk-attachment-renderer-audio"
40
80
  :class="props.className"
41
81
  data-testid="copilot-chat-attachment-renderer-audio"
42
82
  >
@@ -44,11 +84,11 @@ const documentLabel = computed(
44
84
  :src="sourceUrl"
45
85
  controls
46
86
  preload="metadata"
47
- class="cpk:max-w-[300px] cpk:w-full cpk:h-10"
87
+ class="cpk-attachment-renderer-audio-player"
48
88
  />
49
89
  <span
50
90
  v-if="props.filename"
51
- class="cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]"
91
+ class="cpk-attachment-renderer-audio-filename"
52
92
  data-testid="copilot-chat-attachment-renderer-audio-filename"
53
93
  >
54
94
  {{ props.filename }}
@@ -60,28 +100,37 @@ const documentLabel = computed(
60
100
  :src="sourceUrl"
61
101
  controls
62
102
  preload="metadata"
63
- class="cpk:max-w-[400px] cpk:w-full cpk:rounded-lg"
103
+ class="cpk-attachment-renderer cpk-attachment-renderer-video"
64
104
  :class="props.className"
65
105
  data-testid="copilot-chat-attachment-renderer-video"
66
106
  />
67
107
 
68
- <div
108
+ <!-- `download` is honoured for same-origin, data: and blob: URLs. Browsers
109
+ ignore it for cross-origin URLs unless the server sends
110
+ `Content-Disposition: attachment`; `target="_blank"` keeps those from
111
+ navigating the chat away and opens the file in a new tab instead. -->
112
+ <a
69
113
  v-else
70
- class="cpk:inline-flex cpk:items-center cpk:gap-2 cpk:px-3 cpk:py-2 cpk:border cpk:border-border cpk:rounded-lg cpk:bg-muted"
114
+ :href="sourceUrl"
115
+ :download="props.filename ?? ''"
116
+ target="_blank"
117
+ rel="noopener noreferrer"
118
+ :aria-label="documentAriaLabel"
119
+ class="cpk-attachment-renderer cpk-attachment-renderer-document"
71
120
  :class="props.className"
72
121
  data-testid="copilot-chat-attachment-renderer-document"
73
122
  >
74
123
  <span
75
- class="cpk:text-xs cpk:font-bold cpk:uppercase"
124
+ class="cpk-attachment-renderer-document-icon"
76
125
  data-testid="copilot-chat-attachment-renderer-document-icon"
77
126
  >
78
127
  {{ getDocumentIcon(props.source.mimeType ?? "") }}
79
128
  </span>
80
129
  <span
81
- class="cpk:text-sm cpk:text-muted-foreground cpk:truncate"
130
+ class="cpk-attachment-renderer-document-name"
82
131
  data-testid="copilot-chat-attachment-renderer-document-label"
83
132
  >
84
133
  {{ documentLabel }}
85
134
  </span>
86
- </div>
135
+ </a>
87
136
  </template>
@@ -263,7 +263,7 @@ defineExpose({
263
263
  </script>
264
264
 
265
265
  <template>
266
- <div data-copilotkit class="cpk:w-full cpk:px-5 cpk:py-3" v-bind="attrs">
267
- <canvas ref="canvasRef" class="cpk:block cpk:h-[26px] cpk:w-full" />
266
+ <div data-copilotkit class="cpk-audio-recorder" v-bind="attrs">
267
+ <canvas ref="canvasRef" class="cpk-audio-recorder-canvas" />
268
268
  </div>
269
269
  </template>