@copilotkit/vue 2.0.0-rc.3 → 2.0.0-rc.31

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 (343) hide show
  1. package/CONTRIBUTOR_GUIDE.md +1 -1
  2. package/PARITY.md +3 -3
  3. package/dist/hooks/use-copilot-action.d.ts.map +1 -1
  4. package/dist/hooks/use-frontend-tool.d.ts +1 -1
  5. package/dist/hooks/use-frontend-tool.d.ts.map +1 -1
  6. package/dist/hooks/zod-parameters.d.ts +14 -0
  7. package/dist/hooks/zod-parameters.d.ts.map +1 -0
  8. package/dist/index.cjs +1 -73
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.mjs +365 -16920
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/use-render-activity-message-BMTHA_P2.js +15901 -0
  13. package/dist/use-render-activity-message-BMTHA_P2.js.map +1 -0
  14. package/dist/use-render-activity-message-CH3beu7z.cjs +113 -0
  15. package/dist/use-render-activity-message-CH3beu7z.cjs.map +1 -0
  16. package/dist/v2/components/A2UIMessageRenderer.d.ts +14 -0
  17. package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
  18. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts +10 -3
  19. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
  20. package/dist/v2/components/CopilotKitInspector.vue.d.ts +5 -1
  21. package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -1
  22. package/dist/v2/components/InlineFeatureWarning.vue.d.ts.map +1 -1
  23. package/dist/v2/components/LicenseWarningBanner.vue.d.ts +2 -2
  24. package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -1
  25. package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
  26. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +13 -59
  27. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -1
  28. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +3 -11
  29. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
  30. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +10 -15
  31. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -1
  32. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +14 -130
  33. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -1
  34. package/dist/v2/components/a2ui/catalog.d.ts.map +1 -1
  35. package/dist/v2/components/a2ui/utils.d.ts +3 -15
  36. package/dist/v2/components/a2ui/utils.d.ts.map +1 -1
  37. package/dist/v2/components/a2ui.d.ts +9 -8
  38. package/dist/v2/components/a2ui.d.ts.map +1 -1
  39. package/dist/v2/components/chat/CopilotChat.vue.d.ts +2 -2
  40. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  41. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  42. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  43. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  44. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts +4 -8
  45. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
  46. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts +1 -1
  47. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -1
  48. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +7 -286
  49. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  50. package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts.map +1 -1
  51. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -1
  52. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +1 -1
  53. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  54. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  55. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +1 -1
  56. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  57. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  58. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +1 -1
  59. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  60. package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -1
  61. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +3 -3
  62. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  63. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -1
  64. package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
  65. package/dist/v2/components/chat/CopilotPopup.vue.d.ts +8 -3
  66. package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -1
  67. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +5 -3
  68. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -1
  69. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +5 -3
  70. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  71. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +8 -3
  72. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -1
  73. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +6 -4
  74. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -1
  75. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +6 -4
  76. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  77. package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts.map +1 -1
  78. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts +1 -0
  79. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
  80. package/dist/v2/components/chat/audioRecorder.d.ts +3 -4
  81. package/dist/v2/components/chat/audioRecorder.d.ts.map +1 -1
  82. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +4 -4
  83. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  84. package/dist/v2/components/chat/forwarded-slots.d.ts +12 -0
  85. package/dist/v2/components/chat/forwarded-slots.d.ts.map +1 -0
  86. package/dist/v2/components/chat/index.d.ts +34 -31
  87. package/dist/v2/components/chat/index.d.ts.map +1 -1
  88. package/dist/v2/components/chat/inspector-shortcuts.d.ts +2 -1
  89. package/dist/v2/components/chat/inspector-shortcuts.d.ts.map +1 -1
  90. package/dist/v2/components/chat/message-feedback.d.ts +38 -49
  91. package/dist/v2/components/chat/message-feedback.d.ts.map +1 -1
  92. package/dist/v2/components/chat/tool-call-display-context.d.ts +4 -19
  93. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  94. package/dist/v2/components/chat/tool-call-group-parts.d.ts +7 -20
  95. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  96. package/dist/v2/components/chat/turn-work.d.ts +0 -2
  97. package/dist/v2/components/chat/turn-work.d.ts.map +1 -1
  98. package/dist/v2/components/chat/types.d.ts +9 -4
  99. package/dist/v2/components/chat/types.d.ts.map +1 -1
  100. package/dist/v2/components/chat/use-chat-labels.d.ts +8 -0
  101. package/dist/v2/components/chat/use-chat-labels.d.ts.map +1 -0
  102. package/dist/v2/components/chat/use-copy-feedback.d.ts +11 -0
  103. package/dist/v2/components/chat/use-copy-feedback.d.ts.map +1 -0
  104. package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -1
  105. package/dist/v2/components/icons/index.d.ts +968 -1
  106. package/dist/v2/components/icons/index.d.ts.map +1 -1
  107. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +1 -1
  108. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -1
  109. package/dist/v2/hooks/use-agent-context.d.ts +2 -3
  110. package/dist/v2/hooks/use-agent-context.d.ts.map +1 -1
  111. package/dist/v2/hooks/use-agent.d.ts +4 -1
  112. package/dist/v2/hooks/use-agent.d.ts.map +1 -1
  113. package/dist/v2/hooks/use-attachments.d.ts +7 -0
  114. package/dist/v2/hooks/use-attachments.d.ts.map +1 -1
  115. package/dist/v2/hooks/use-capabilities.d.ts.map +1 -1
  116. package/dist/v2/hooks/use-component.d.ts +1 -0
  117. package/dist/v2/hooks/use-component.d.ts.map +1 -1
  118. package/dist/v2/hooks/use-configure-suggestions.d.ts +4 -9
  119. package/dist/v2/hooks/use-configure-suggestions.d.ts.map +1 -1
  120. package/dist/v2/hooks/use-default-render-tool.d.ts +2 -7
  121. package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
  122. package/dist/v2/hooks/use-frontend-tool.d.ts +5 -0
  123. package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
  124. package/dist/v2/hooks/use-human-in-the-loop.d.ts +2 -1
  125. package/dist/v2/hooks/use-human-in-the-loop.d.ts.map +1 -1
  126. package/dist/v2/hooks/use-interrupt.d.ts +0 -1
  127. package/dist/v2/hooks/use-interrupt.d.ts.map +1 -1
  128. package/dist/v2/hooks/use-keyboard-height.d.ts +1 -1
  129. package/dist/v2/hooks/use-keyboard-height.d.ts.map +1 -1
  130. package/dist/v2/hooks/use-pin-to-send.d.ts +4 -12
  131. package/dist/v2/hooks/use-pin-to-send.d.ts.map +1 -1
  132. package/dist/v2/hooks/use-render-activity-message.d.ts +13 -9
  133. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  134. package/dist/v2/hooks/use-render-custom-messages.d.ts +5 -7
  135. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  136. package/dist/v2/hooks/use-render-tool.d.ts +1 -1
  137. package/dist/v2/hooks/use-render-tool.d.ts.map +1 -1
  138. package/dist/v2/hooks/use-suggestions.d.ts.map +1 -1
  139. package/dist/v2/hooks/use-threads.d.ts +6 -23
  140. package/dist/v2/hooks/use-threads.d.ts.map +1 -1
  141. package/dist/v2/index.cjs +1 -1
  142. package/dist/v2/index.mjs +197 -200
  143. package/dist/v2/index.mjs.map +1 -1
  144. package/dist/v2/lib/processPartialHtml.d.ts +1 -2
  145. package/dist/v2/lib/processPartialHtml.d.ts.map +1 -1
  146. package/dist/v2/lib/shallow-stable.d.ts.map +1 -1
  147. package/dist/v2/lib/vue-core.d.ts +33 -19
  148. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  149. package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
  150. package/dist/v2/providers/CopilotKitProvider.types.d.ts +9 -129
  151. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  152. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +9 -10
  153. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  154. package/dist/v2/providers/chat-configuration-store-key.d.ts +8 -0
  155. package/dist/v2/providers/chat-configuration-store-key.d.ts.map +1 -0
  156. package/dist/v2/providers/markdown-renderer.d.ts +4 -2
  157. package/dist/v2/providers/markdown-renderer.d.ts.map +1 -1
  158. package/dist/v2/providers/types.d.ts +3 -46
  159. package/dist/v2/providers/types.d.ts.map +1 -1
  160. package/dist/v2/providers/use-inspector-thread-override.d.ts +5 -0
  161. package/dist/v2/providers/use-inspector-thread-override.d.ts.map +1 -1
  162. package/dist/v2/types/defineToolCallRenderer.d.ts +1 -1
  163. package/dist/v2/types/defineToolCallRenderer.d.ts.map +1 -1
  164. package/dist/v2/types/human-in-the-loop.d.ts +8 -29
  165. package/dist/v2/types/human-in-the-loop.d.ts.map +1 -1
  166. package/dist/v2/types/interrupt.d.ts +1 -22
  167. package/dist/v2/types/interrupt.d.ts.map +1 -1
  168. package/dist/v2/types/vue-activity-message-renderer.d.ts +16 -15
  169. package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -1
  170. package/dist/v2/types/vue-custom-message-renderer.d.ts +7 -16
  171. package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -1
  172. package/dist/v2/types/vue-tool-call-renderer.d.ts +16 -35
  173. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  174. package/package.json +12 -13
  175. package/src/__tests__/build-entries.test.ts +29 -0
  176. package/src/hooks/__tests__/zod-parameters.test.ts +520 -0
  177. package/src/hooks/use-copilot-action.ts +2 -1
  178. package/src/hooks/use-frontend-tool.ts +3 -6
  179. package/src/hooks/zod-parameters.ts +162 -0
  180. package/src/v2/__tests__/exports.test.ts +10 -0
  181. package/src/v2/__tests__/mocks/web-inspector.ts +6 -0
  182. package/src/v2/__tests__/utils/test-helpers.ts +34 -4
  183. package/src/v2/components/A2UIMessageRenderer.ts +98 -68
  184. package/src/v2/components/A2UISurfaceActivityRenderer.vue +68 -103
  185. package/src/v2/components/CopilotKitInspector.vue +104 -51
  186. package/src/v2/components/InlineFeatureWarning.vue +20 -21
  187. package/src/v2/components/LicenseWarningBanner.vue +27 -135
  188. package/src/v2/components/MCPAppsActivityRenderer.ts +75 -201
  189. package/src/v2/components/OpenGenerativeUIRenderer.ts +122 -446
  190. package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +171 -8
  191. package/src/v2/components/__tests__/CopilotKitInspector.test.ts +42 -0
  192. package/src/v2/components/__tests__/LicenseWarningBanner.test.ts +60 -0
  193. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +64 -8
  194. package/src/v2/components/__tests__/OpenGenerativeUIToolRenderer.test.ts +38 -0
  195. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +6 -26
  196. package/src/v2/components/a2ui/A2UICatalogContext.ts +27 -185
  197. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +127 -327
  198. package/src/v2/components/a2ui/catalog.ts +3 -3
  199. package/src/v2/components/a2ui/utils.ts +13 -65
  200. package/src/v2/components/a2ui.ts +15 -22
  201. package/src/v2/components/chat/CopilotChat.vue +121 -498
  202. package/src/v2/components/chat/CopilotChatAssistantMessage.vue +47 -184
  203. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +36 -55
  204. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +14 -6
  205. package/src/v2/components/chat/CopilotChatAudioRecorder.vue +19 -246
  206. package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +12 -77
  207. package/src/v2/components/chat/CopilotChatInput.vue +139 -654
  208. package/src/v2/components/chat/CopilotChatInspectorButton.vue +45 -108
  209. package/src/v2/components/chat/CopilotChatLightbox.vue +10 -31
  210. package/src/v2/components/chat/CopilotChatMessageView.vue +96 -274
  211. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +27 -62
  212. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +63 -71
  213. package/src/v2/components/chat/CopilotChatToolCallItem.vue +8 -6
  214. package/src/v2/components/chat/CopilotChatToolCallsView.vue +31 -32
  215. package/src/v2/components/chat/CopilotChatUserMessage.vue +24 -112
  216. package/src/v2/components/chat/CopilotChatView.vue +123 -161
  217. package/src/v2/components/chat/CopilotChatWorkSummary.vue +26 -36
  218. package/src/v2/components/chat/CopilotModalHeader.vue +11 -23
  219. package/src/v2/components/chat/CopilotPopup.vue +23 -25
  220. package/src/v2/components/chat/CopilotPopupView.vue +10 -39
  221. package/src/v2/components/chat/CopilotPopupViewInternal.vue +49 -185
  222. package/src/v2/components/chat/CopilotSidebar.vue +23 -25
  223. package/src/v2/components/chat/CopilotSidebarView.vue +10 -39
  224. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +56 -100
  225. package/src/v2/components/chat/CopilotThreadsDrawer.vue +43 -105
  226. package/src/v2/components/chat/StreamingMarkdownDefault.vue +15 -28
  227. package/src/v2/components/chat/__tests__/CopilotChat.labels.test.ts +19 -4
  228. package/src/v2/components/chat/__tests__/CopilotChat.test.ts +12 -10
  229. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.ts +26 -9
  230. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.learningFeedback.test.ts +5 -5
  231. package/src/v2/components/chat/__tests__/CopilotChatAttachmentQueue.test.ts +28 -0
  232. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +132 -1
  233. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +247 -403
  234. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +1 -99
  235. package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.ts +43 -1
  236. package/src/v2/components/chat/__tests__/CopilotPopup.test.ts +90 -1
  237. package/src/v2/components/chat/__tests__/CopilotPopupView.test.ts +19 -0
  238. package/src/v2/components/chat/__tests__/CopilotSidebar.test.ts +90 -1
  239. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +32 -8
  240. package/src/v2/components/chat/__tests__/CopilotSidebarView.test.ts +2 -1
  241. package/src/v2/components/chat/__tests__/StreamingMarkdownDefault.test.ts +54 -0
  242. package/src/v2/components/chat/audioRecorder.ts +4 -7
  243. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +25 -90
  244. package/src/v2/components/chat/forwarded-slots.ts +17 -0
  245. package/src/v2/components/chat/index.ts +0 -1
  246. package/src/v2/components/chat/inspector-shortcuts.ts +10 -16
  247. package/src/v2/components/chat/message-feedback.ts +92 -195
  248. package/src/v2/components/chat/tool-call-display-context.ts +6 -22
  249. package/src/v2/components/chat/tool-call-group-parts.ts +47 -108
  250. package/src/v2/components/chat/turn-work.ts +0 -9
  251. package/src/v2/components/chat/types.ts +12 -4
  252. package/src/v2/components/chat/use-chat-labels.ts +16 -0
  253. package/src/v2/components/chat/use-copy-feedback.ts +21 -0
  254. package/src/v2/components/chat/use-lightbox.ts +15 -33
  255. package/src/v2/components/icons/__tests__/icons.test.ts +72 -68
  256. package/src/v2/components/icons/index.ts +109 -27
  257. package/src/v2/hooks/__tests__/use-agent-error-state.test.ts +5 -3
  258. package/src/v2/hooks/__tests__/use-agent-stability.test.ts +25 -4
  259. package/src/v2/hooks/__tests__/use-agent-throttle.test.ts +5 -1
  260. package/src/v2/hooks/__tests__/use-agent.test.ts +6 -3
  261. package/src/v2/hooks/__tests__/use-component.test.ts +21 -0
  262. package/src/v2/hooks/__tests__/use-configure-suggestions.test.ts +30 -1
  263. package/src/v2/hooks/__tests__/use-default-render-tool.test.ts +10 -10
  264. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +66 -1
  265. package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.ts +6 -1
  266. package/src/v2/hooks/__tests__/use-human-in-the-loop.test.ts +187 -12
  267. package/src/v2/hooks/__tests__/use-interrupt.test.ts +97 -0
  268. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +106 -0
  269. package/src/v2/hooks/__tests__/use-render-tool.test.ts +3 -6
  270. package/src/v2/hooks/__tests__/use-threads.test.ts +48 -6
  271. package/src/v2/hooks/__tests__/zod-regression.test.ts +5 -4
  272. package/src/v2/hooks/use-agent-context.ts +4 -10
  273. package/src/v2/hooks/use-agent.ts +86 -290
  274. package/src/v2/hooks/use-attachments.ts +48 -168
  275. package/src/v2/hooks/use-capabilities.ts +2 -7
  276. package/src/v2/hooks/use-component.ts +7 -4
  277. package/src/v2/hooks/use-configure-suggestions.ts +29 -220
  278. package/src/v2/hooks/use-default-render-tool.ts +31 -152
  279. package/src/v2/hooks/use-frontend-tool.ts +15 -25
  280. package/src/v2/hooks/use-human-in-the-loop.ts +11 -80
  281. package/src/v2/hooks/use-interrupt.ts +29 -308
  282. package/src/v2/hooks/use-keyboard-height.ts +17 -40
  283. package/src/v2/hooks/use-pin-to-send.ts +15 -99
  284. package/src/v2/hooks/use-render-activity-message.ts +35 -57
  285. package/src/v2/hooks/use-render-custom-messages.ts +24 -88
  286. package/src/v2/hooks/use-render-tool.ts +2 -4
  287. package/src/v2/hooks/use-suggestions.ts +17 -36
  288. package/src/v2/hooks/use-threads.ts +49 -288
  289. package/src/v2/lib/__tests__/shallow-stable.test.ts +54 -0
  290. package/src/v2/lib/__tests__/vue-core-renderers.test.ts +42 -0
  291. package/src/v2/lib/processPartialHtml.ts +6 -21
  292. package/src/v2/lib/shallow-stable.ts +2 -16
  293. package/src/v2/lib/vue-core.ts +90 -85
  294. package/src/v2/providers/CopilotChatConfigurationProvider.vue +48 -186
  295. package/src/v2/providers/CopilotKitProvider.types.ts +12 -132
  296. package/src/v2/providers/CopilotKitProvider.vue +291 -641
  297. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.ts +22 -2
  298. package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +38 -2
  299. package/src/v2/providers/__tests__/CopilotKitProvider.config.test.ts +124 -0
  300. package/src/v2/providers/__tests__/CopilotKitProvider.headersAtSendTime.test.ts +3 -3
  301. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +3 -1
  302. package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.ts +4 -1
  303. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +88 -28
  304. package/src/v2/providers/chat-configuration-store-key.ts +9 -0
  305. package/src/v2/providers/markdown-renderer.ts +8 -2
  306. package/src/v2/providers/types.ts +3 -49
  307. package/src/v2/providers/use-inspector-thread-override.ts +22 -80
  308. package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +28 -33
  309. package/src/v2/types/defineToolCallRenderer.ts +7 -6
  310. package/src/v2/types/human-in-the-loop.ts +8 -32
  311. package/src/v2/types/interrupt.ts +7 -31
  312. package/src/v2/types/vue-activity-message-renderer.ts +24 -15
  313. package/src/v2/types/vue-custom-message-renderer.ts +12 -16
  314. package/src/v2/types/vue-tool-call-renderer.ts +23 -38
  315. package/tsconfig.json +0 -1
  316. package/vite.config.ts +8 -5
  317. package/vitest.config.ts +12 -7
  318. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts +0 -9
  319. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +0 -1
  320. package/dist/v2/components/chat/normalize-auto-scroll.d.ts +0 -3
  321. package/dist/v2/components/chat/normalize-auto-scroll.d.ts.map +0 -1
  322. package/dist/v2/components/chat/tool-call-layout.d.ts +0 -115
  323. package/dist/v2/components/chat/tool-call-layout.d.ts.map +0 -1
  324. package/dist/v2/lib/is-mobile-viewport.d.ts +0 -15
  325. package/dist/v2/lib/is-mobile-viewport.d.ts.map +0 -1
  326. package/dist/v2/lib/message-feedback.d.ts +0 -33
  327. package/dist/v2/lib/message-feedback.d.ts.map +0 -1
  328. package/dist/v2/lib/transcription-client.d.ts +0 -19
  329. package/dist/v2/lib/transcription-client.d.ts.map +0 -1
  330. package/src/v2/components/__tests__/A2UICatalogContext.test.ts +0 -63
  331. package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +0 -175
  332. package/src/v2/components/chat/BasicMarkdown.vue +0 -160
  333. package/src/v2/components/chat/__tests__/BasicMarkdown.test.ts +0 -69
  334. package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +0 -39
  335. package/src/v2/components/chat/normalize-auto-scroll.ts +0 -17
  336. package/src/v2/components/chat/tool-call-layout.ts +0 -304
  337. package/src/v2/lib/__tests__/is-mobile-viewport.test.ts +0 -48
  338. package/src/v2/lib/__tests__/message-feedback.test.ts +0 -152
  339. package/src/v2/lib/__tests__/processPartialHtml.test.ts +0 -84
  340. package/src/v2/lib/__tests__/transcription-client.test.ts +0 -67
  341. package/src/v2/lib/is-mobile-viewport.ts +0 -23
  342. package/src/v2/lib/message-feedback.ts +0 -120
  343. package/src/v2/lib/transcription-client.ts +0 -151
@@ -5,11 +5,19 @@ import {
5
5
  onBeforeUnmount,
6
6
  onMounted,
7
7
  ref,
8
+ shallowRef,
8
9
  useAttrs,
9
10
  watch,
10
11
  } from "vue";
11
- import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
12
- import { CopilotChatDefaultLabels } from "../../providers/types";
12
+ import {
13
+ ɵComposerController,
14
+ ɵcomposerButtonState,
15
+ ɵcreateComposerAutosize,
16
+ ɵflattenToolsMenuForDisplay,
17
+ ɵnormalizeToolsMenu,
18
+ ɵshowsComposerDisclaimer,
19
+ ɵsyncRecorderToMode,
20
+ } from "@copilotkit/core";
13
21
  import {
14
22
  IconArrowUp,
15
23
  IconCheck,
@@ -29,20 +37,11 @@ import type {
29
37
  import { useCopilotId } from "../ui/use-id";
30
38
  import type { CopilotChatAudioRecorderRef } from "./audioRecorder";
31
39
  import type { CopilotChatInputMode, ToolsMenuItem } from "./types";
40
+ import { useChatLabels } from "./use-chat-labels";
32
41
 
33
42
  defineOptions({ inheritAttrs: false });
34
43
 
35
44
  type MenuEntry = ToolsMenuItem | "-";
36
- type MenuDisplayEntry =
37
- | { type: "separator"; key: string }
38
- | { type: "label"; key: string; label: string; depth: number }
39
- | {
40
- type: "item";
41
- key: string;
42
- label: string;
43
- depth: number;
44
- action: () => void;
45
- };
46
45
 
47
46
  const props = withDefaults(
48
47
  defineProps<{
@@ -107,7 +106,6 @@ const emit = defineEmits<{
107
106
  }>();
108
107
 
109
108
  const attrs = useAttrs();
110
- const config = useCopilotChatConfiguration();
111
109
  const shellRef = ref<HTMLElement | null>(null);
112
110
  const textareaRef = ref<HTMLTextAreaElement | null>(null);
113
111
  const gridRef = ref<HTMLElement | null>(null);
@@ -115,12 +113,31 @@ const addButtonContainerRef = ref<HTMLElement | null>(null);
115
113
  const actionsContainerRef = ref<HTMLElement | null>(null);
116
114
  const slashMenuRef = ref<HTMLElement | null>(null);
117
115
  const audioRecorderRef = ref<CopilotChatAudioRecorderRef | null>(null);
118
- const localValue = ref(props.modelValue ?? "");
119
116
  const isComposing = ref(false);
120
- const layout = ref<"compact" | "expanded">("compact");
121
- const commandQuery = ref<string | null>(null);
122
- const slashHighlightIndex = ref(0);
123
- const previousCommandQuery = ref<string | null>(null);
117
+ // The text, controlled or not, and the slash-command menu: see core's
118
+ // ɵComposerController.
119
+ const composer = new ɵComposerController<ToolsMenuItem>({
120
+ initialValue: props.modelValue ?? "",
121
+ onChange: (value) => emit("update:modelValue", value),
122
+ });
123
+ const composerState = shallowRef(composer.getSnapshot());
124
+ composer.subscribe(() => {
125
+ composerState.value = composer.getSnapshot();
126
+ });
127
+ // Sizes the text box and picks the compact or expanded layout.
128
+ const autosize = ɵcreateComposerAutosize({
129
+ getElements: () => ({
130
+ textarea: textareaRef.value,
131
+ grid: gridRef.value,
132
+ start: addButtonContainerRef.value,
133
+ actions: actionsContainerRef.value,
134
+ }),
135
+ maxRows: props.maxRows,
136
+ });
137
+ const layout = shallowRef(autosize.getSnapshot());
138
+ autosize.subscribe(() => {
139
+ layout.value = autosize.getSnapshot();
140
+ });
124
141
  const addMenuOpen = ref(false);
125
142
  // Where focus goes as the add menu opens: its first or last item for the
126
143
  // keyboard, the menu itself for a pointer, as Radix's DropdownMenu does.
@@ -128,38 +145,27 @@ const addMenuInitialFocus = ref<"first" | "last" | "content">("content");
128
145
  const addMenuAnchor = ref<HTMLElement | null>(null);
129
146
  const addMenuId = useCopilotId("cpk-add-menu");
130
147
  const addMenuTriggerId = `${addMenuId}-trigger`;
131
- const measurements = ref({
132
- singleLineHeight: 0,
133
- maxHeight: 0,
134
- paddingLeft: 0,
135
- paddingRight: 0,
136
- });
137
- const resizeEvaluationRafRef = ref<number | null>(null);
138
- const ignoreResizeRef = ref(false);
139
- const measurementCanvasRef = ref<HTMLCanvasElement | null>(null);
140
- const containerCacheRef = ref<{
141
- compactWidth: number;
142
- } | null>(null);
143
- const didWarnMissingFontRef = ref(false);
144
- const didWarnMissingCanvasContextRef = ref(false);
145
- let resizeObserver: ResizeObserver | null = null;
146
148
 
147
- const isControlled = computed(() => props.modelValue !== undefined);
148
- const inputValue = computed(() =>
149
- isControlled.value ? (props.modelValue ?? "") : localValue.value,
149
+ const inputValue = computed(() => composerState.value.value);
150
+ const filteredCommands = computed(() => composerState.value.slashCommands);
151
+ const slashHighlightIndex = computed(
152
+ () => composerState.value.slashHighlightIndex,
150
153
  );
151
- const labels = computed(() => config.value?.labels ?? CopilotChatDefaultLabels);
154
+ const slashMenuVisible = computed(
155
+ () => composerState.value.slashQuery !== null,
156
+ );
157
+ const labels = useChatLabels();
152
158
  const resolvedPlaceholder = computed(
153
159
  () => props.placeholder ?? labels.value.chatInputPlaceholder,
154
160
  );
155
161
  const isExpanded = computed(
156
162
  () => props.mode === "input" && layout.value === "expanded",
157
163
  );
158
- const isProcessing = computed(
159
- () => props.mode !== "transcribe" && props.isRunning,
160
- );
161
- const shouldShowDisclaimer = computed(
162
- () => props.showDisclaimer ?? props.positioning === "absolute",
164
+ const shouldShowDisclaimer = computed(() =>
165
+ ɵshowsComposerDisclaimer({
166
+ showDisclaimer: props.showDisclaimer,
167
+ positioning: props.positioning,
168
+ }),
163
169
  );
164
170
  const hasSubmitAction = computed(
165
171
  () => typeof props.onSubmitMessage === "function",
@@ -177,16 +183,20 @@ const hasFinishTranscribeAction = computed(
177
183
  typeof props.onFinishTranscribe === "function" ||
178
184
  typeof props.onFinishTranscribeWithAudio === "function",
179
185
  );
180
- const canSend = computed(
181
- () =>
182
- props.mode === "input" &&
183
- !props.disabled &&
184
- hasSubmitAction.value &&
185
- inputValue.value.trim().length > 0,
186
- );
187
- const sendDisabled = computed(() =>
188
- isProcessing.value ? !hasStopAction.value : !canSend.value,
186
+ const composerInput = computed(() => ({
187
+ mode: props.mode,
188
+ isRunning: props.isRunning,
189
+ value: inputValue.value,
190
+ canSubmit: hasSubmitAction.value,
191
+ canStop: hasStopAction.value,
192
+ disabled: props.disabled,
193
+ }));
194
+ const composerButton = computed(() =>
195
+ ɵcomposerButtonState(composerInput.value),
189
196
  );
197
+ const isProcessing = computed(() => composerButton.value.isProcessing);
198
+ const sendDisabled = computed(() => composerButton.value.disabled);
199
+ const showsStop = computed(() => composerButton.value.action === "stop");
190
200
 
191
201
  const containerClass = computed(() => [
192
202
  "cpk-chat-input",
@@ -200,288 +210,98 @@ const rootAttrs = computed(() => {
200
210
  return rest;
201
211
  });
202
212
 
203
- function isMenuGroup(
204
- item: ToolsMenuItem,
205
- ): item is ToolsMenuItem & { items: MenuEntry[] } {
206
- return Array.isArray((item as ToolsMenuItem).items);
207
- }
208
-
209
- function createDefaultAddItem(): ToolsMenuItem | null {
210
- if (!hasAddFileAction.value) {
211
- return null;
212
- }
213
- return {
214
- label: labels.value.chatInputToolbarAddButtonLabel,
215
- action: () => emit("add-file"),
216
- };
217
- }
218
-
219
- function normalizeMenuItems() {
220
- const items: MenuEntry[] = [];
221
- const addItem = createDefaultAddItem();
222
- if (addItem) {
223
- items.push(addItem);
224
- }
225
-
226
- if (props.toolsMenu.length > 0) {
227
- if (items.length > 0) {
228
- items.push("-");
229
- }
230
-
231
- for (const menuEntry of props.toolsMenu) {
232
- if (menuEntry === "-") {
233
- if (items.length === 0 || items[items.length - 1] === "-") {
234
- continue;
213
+ const menuItems = computed(() =>
214
+ ɵnormalizeToolsMenu<ToolsMenuItem>(
215
+ props.toolsMenu,
216
+ hasAddFileAction.value
217
+ ? {
218
+ label: labels.value.chatInputToolbarAddButtonLabel,
219
+ action: () => emit("add-file"),
235
220
  }
236
- items.push("-");
237
- continue;
238
- }
239
-
240
- items.push(menuEntry);
241
- }
242
- }
243
-
244
- while (items.length > 0 && items[items.length - 1] === "-") {
245
- items.pop();
246
- }
247
-
248
- return items;
249
- }
250
-
251
- const menuItems = computed(() => normalizeMenuItems());
221
+ : undefined,
222
+ ),
223
+ );
252
224
  const hasMenuItems = computed(() => menuItems.value.length > 0);
225
+ const menuDisplayItems = computed(() =>
226
+ ɵflattenToolsMenuForDisplay(menuItems.value),
227
+ );
253
228
 
254
- function flattenMenuForCommands(items: MenuEntry[]) {
255
- const seen = new Set<string>();
256
- const commands: ToolsMenuItem[] = [];
257
-
258
- const walk = (entryList: MenuEntry[]) => {
259
- for (const entry of entryList) {
260
- if (entry === "-") {
261
- continue;
262
- }
263
-
264
- if (isMenuGroup(entry) && entry.items.length > 0) {
265
- walk(entry.items);
266
- continue;
267
- }
268
-
269
- if (!entry.action || seen.has(entry.label)) {
270
- continue;
271
- }
272
-
273
- seen.add(entry.label);
274
- commands.push(entry);
275
- }
276
- };
277
-
278
- walk(items);
279
- return commands;
280
- }
281
-
282
- const commandItems = computed(() => flattenMenuForCommands(menuItems.value));
283
-
284
- function flattenMenuDisplay(items: MenuEntry[], depth = 0, prefix = "root") {
285
- const entries: MenuDisplayEntry[] = [];
286
- for (const [index, item] of items.entries()) {
287
- const key = `${prefix}-${index}`;
288
- if (item === "-") {
289
- entries.push({ type: "separator", key: `sep-${key}` });
290
- continue;
291
- }
292
- if (isMenuGroup(item) && item.items.length > 0) {
293
- entries.push({
294
- type: "label",
295
- key: `label-${key}`,
296
- label: item.label,
297
- depth,
298
- });
299
- entries.push(...flattenMenuDisplay(item.items, depth + 1, key));
300
- continue;
301
- }
302
- if (item.action) {
303
- entries.push({
304
- type: "item",
305
- key: `item-${key}`,
306
- label: item.label,
307
- depth,
308
- action: item.action,
309
- });
310
- }
311
- }
312
- return entries;
313
- }
314
-
315
- const menuDisplayItems = computed(() => flattenMenuDisplay(menuItems.value));
316
-
317
- const filteredCommands = computed(() => {
318
- if (commandQuery.value === null || commandItems.value.length === 0) {
319
- return [] as ToolsMenuItem[];
320
- }
321
-
322
- const normalized = commandQuery.value.trim().toLowerCase();
323
- if (normalized.length === 0) {
324
- return commandItems.value;
325
- }
326
-
327
- const startsWith: ToolsMenuItem[] = [];
328
- const contains: ToolsMenuItem[] = [];
329
-
330
- for (const command of commandItems.value) {
331
- const label = command.label.toLowerCase();
332
- if (label.startsWith(normalized)) {
333
- startsWith.push(command);
334
- continue;
335
- }
336
- if (label.includes(normalized)) {
337
- contains.push(command);
338
- }
339
- }
340
-
341
- return [...startsWith, ...contains];
342
- });
343
-
344
- const slashMenuVisible = computed(
345
- () => commandQuery.value !== null && commandItems.value.length > 0,
229
+ watch(
230
+ [() => props.modelValue, () => props.mode, menuItems],
231
+ ([value, mode, menu]) => composer.setInputs({ value, mode, menu }),
232
+ { immediate: true },
346
233
  );
347
234
 
348
235
  function updateInputValue(nextValue: string) {
349
- if (!isControlled.value) {
350
- localValue.value = nextValue;
351
- }
352
- emit("update:modelValue", nextValue);
353
- }
354
-
355
- function clearInputValue() {
356
- updateInputValue("");
357
- }
358
-
359
- function updateSlashState(value: string) {
360
- if (commandItems.value.length === 0) {
361
- commandQuery.value = null;
362
- return;
363
- }
364
-
365
- if (value.startsWith("/")) {
366
- const firstLine = value.split(/\r?\n/, 1)[0] ?? "";
367
- commandQuery.value = firstLine.slice(1);
368
- return;
369
- }
370
-
371
- commandQuery.value = null;
236
+ composer.setValue(nextValue);
372
237
  }
373
238
 
374
- function runCommand(command: ToolsMenuItem) {
375
- clearInputValue();
376
- command.action?.();
377
- commandQuery.value = null;
378
- slashHighlightIndex.value = 0;
239
+ function focusTextareaSoon() {
379
240
  requestAnimationFrame(() => {
380
241
  textareaRef.value?.focus();
381
242
  });
382
243
  }
383
244
 
245
+ function runCommand(command: ToolsMenuItem) {
246
+ composer.runCommand(command);
247
+ focusTextareaSoon();
248
+ }
249
+
384
250
  function submit() {
385
251
  if (props.mode !== "input" || props.disabled || !hasSubmitAction.value) {
386
252
  return;
387
253
  }
388
254
  // In controlled mode, parent-updated modelValue can lag one tick behind
389
255
  // the actual textarea value during intense runtime/connect churn.
390
- const rawValue = textareaRef.value?.value ?? inputValue.value;
391
- const trimmed = rawValue.trim();
392
- if (!trimmed) {
393
- return;
394
- }
395
-
396
- emit("submit-message", trimmed);
397
-
398
- if (props.clearOnSubmit) {
399
- clearInputValue();
400
- }
401
-
402
- textareaRef.value?.focus();
256
+ const sent = composer.submit((text) => emit("submit-message", text), {
257
+ value: textareaRef.value?.value ?? inputValue.value,
258
+ clear: props.clearOnSubmit,
259
+ });
260
+ if (sent) textareaRef.value?.focus();
403
261
  }
404
262
 
405
263
  function handleInput(event: Event) {
406
- const nextValue = (event.target as HTMLTextAreaElement).value;
407
- updateInputValue(nextValue);
408
- updateSlashState(nextValue);
264
+ updateInputValue((event.target as HTMLTextAreaElement).value);
409
265
  }
410
266
 
267
+ // The button stops a run whatever the input holds. Enter differs: see
268
+ // handleKeydown.
411
269
  function handleSendButtonClick() {
412
- if (isProcessing.value) {
413
- if (hasStopAction.value) {
414
- emit("stop");
415
- }
270
+ if (showsStop.value) {
271
+ emit("stop");
416
272
  return;
417
273
  }
418
- submit();
274
+ if (!isProcessing.value) {
275
+ submit();
276
+ }
419
277
  }
420
278
 
421
279
  function handleKeydown(event: KeyboardEvent) {
422
- if (props.disabled) {
423
- return;
424
- }
425
-
426
- if (isComposing.value || event.isComposing || event.keyCode === 229) {
280
+ if (props.disabled || props.mode !== "input") {
427
281
  return;
428
282
  }
429
283
 
430
- if (commandQuery.value !== null && props.mode === "input") {
431
- if (event.key === "ArrowDown") {
432
- if (filteredCommands.value.length > 0) {
433
- event.preventDefault();
434
- slashHighlightIndex.value =
435
- slashHighlightIndex.value < 0
436
- ? 0
437
- : (slashHighlightIndex.value + 1) % filteredCommands.value.length;
438
- }
439
- return;
440
- }
441
-
442
- if (event.key === "ArrowUp") {
443
- if (filteredCommands.value.length > 0) {
444
- event.preventDefault();
445
- if (slashHighlightIndex.value < 0) {
446
- slashHighlightIndex.value = filteredCommands.value.length - 1;
447
- } else {
448
- slashHighlightIndex.value =
449
- slashHighlightIndex.value <= 0
450
- ? filteredCommands.value.length - 1
451
- : slashHighlightIndex.value - 1;
452
- }
453
- }
454
- return;
455
- }
456
-
457
- if (event.key === "Enter") {
458
- const selected =
459
- slashHighlightIndex.value >= 0
460
- ? filteredCommands.value[slashHighlightIndex.value]
461
- : undefined;
462
- if (selected) {
463
- event.preventDefault();
464
- runCommand(selected);
465
- return;
466
- }
467
- }
468
-
469
- if (event.key === "Escape") {
470
- event.preventDefault();
471
- commandQuery.value = null;
472
- return;
473
- }
474
- }
475
-
476
- if (event.key === "Enter" && !event.shiftKey && props.mode === "input") {
477
- event.preventDefault();
478
- if (isProcessing.value) {
479
- if (hasStopAction.value) {
480
- emit("stop");
481
- }
482
- return;
483
- }
284
+ // Enter with text sends, also while a run is going. Enter in an empty input
285
+ // stops the run. Read the textarea: a controlled modelValue can lag a tick.
286
+ const { preventDefault, action } = composer.keyDown(
287
+ {
288
+ key: event.key,
289
+ shiftKey: event.shiftKey,
290
+ isComposing: isComposing.value || event.isComposing,
291
+ keyCode: event.keyCode,
292
+ },
293
+ {
294
+ ...composerInput.value,
295
+ value: textareaRef.value?.value ?? inputValue.value,
296
+ },
297
+ );
298
+ if (preventDefault) event.preventDefault();
299
+ if (action === "command") {
300
+ focusTextareaSoon();
301
+ } else if (action === "send") {
484
302
  submit();
303
+ } else if (action === "stop") {
304
+ emit("stop");
485
305
  }
486
306
  }
487
307
 
@@ -593,346 +413,39 @@ function handleContainerClick(event: MouseEvent) {
593
413
  textareaRef.value?.focus();
594
414
  }
595
415
 
596
- function ensureMeasurements() {
597
- const textarea = textareaRef.value;
598
- if (!textarea || isComposing.value) {
599
- return;
600
- }
601
-
602
- const previousValue = textarea.value;
603
- const previousHeight = textarea.style.height;
604
- textarea.style.height = "auto";
605
-
606
- const computedStyle = window.getComputedStyle(textarea);
607
- const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
608
- const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
609
- const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
610
- const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
611
-
612
- textarea.value = "";
613
- const singleLineHeight = textarea.scrollHeight;
614
- textarea.value = previousValue;
615
-
616
- const contentHeight = singleLineHeight - paddingTop - paddingBottom;
617
- const maxHeight = contentHeight * props.maxRows + paddingTop + paddingBottom;
618
-
619
- measurements.value = {
620
- singleLineHeight,
621
- maxHeight,
622
- paddingLeft,
623
- paddingRight,
624
- };
625
-
626
- textarea.style.height = previousHeight;
627
- textarea.style.maxHeight = `${maxHeight}px`;
628
- }
629
-
630
- function adjustTextareaHeight() {
631
- const textarea = textareaRef.value;
632
- if (!textarea) {
633
- return 0;
634
- }
635
-
636
- if (measurements.value.singleLineHeight === 0) {
637
- ensureMeasurements();
638
- }
639
-
640
- const { maxHeight } = measurements.value;
641
- if (maxHeight) {
642
- textarea.style.maxHeight = `${maxHeight}px`;
643
- }
644
-
645
- textarea.style.height = "auto";
646
- const scrollHeight = textarea.scrollHeight;
647
- textarea.style.height = `${maxHeight ? Math.min(scrollHeight, maxHeight) : scrollHeight}px`;
648
- return scrollHeight;
649
- }
650
-
651
- function updateLayout(nextLayout: "compact" | "expanded") {
652
- if (layout.value === nextLayout) {
653
- return;
654
- }
655
- ignoreResizeRef.value = true;
656
- layout.value = nextLayout;
657
- }
658
-
659
- function resolveTextareaFont(textarea: HTMLTextAreaElement): string | null {
660
- const textareaStyles = window.getComputedStyle(textarea);
661
- if (textareaStyles.font?.trim()) {
662
- return textareaStyles.font;
663
- }
664
-
665
- if (textareaStyles.fontSize && textareaStyles.fontFamily) {
666
- const fallbackFont =
667
- `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ` +
668
- `${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ` +
669
- `${textareaStyles.fontFamily}`;
670
- if (fallbackFont.trim()) {
671
- return fallbackFont;
672
- }
673
- }
674
-
675
- if (process.env.NODE_ENV !== "production" && !didWarnMissingFontRef.value) {
676
- didWarnMissingFontRef.value = true;
677
- console.warn(
678
- "[CopilotChatInput] Could not resolve textarea font for layout measurement. " +
679
- "Text-width-based expansion will be skipped until the next container resize.",
680
- );
681
- }
682
- return null;
683
- }
684
-
685
- function updateContainerCache() {
686
- const grid = gridRef.value;
687
- const addContainer = addButtonContainerRef.value;
688
- const actionsContainer = actionsContainerRef.value;
689
- if (!grid || !addContainer || !actionsContainer) {
690
- containerCacheRef.value = null;
691
- return null;
692
- }
693
-
694
- const gridStyles = window.getComputedStyle(grid);
695
- const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
696
- const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
697
- const columnGap = parseFloat(gridStyles.columnGap) || 0;
698
- const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
699
- if (gridAvailableWidth <= 0) {
700
- containerCacheRef.value = null;
701
- return null;
702
- }
703
-
704
- const addWidth = addContainer.getBoundingClientRect().width;
705
- const actionsWidth = actionsContainer.getBoundingClientRect().width;
706
- const compactWidth = Math.max(
707
- gridAvailableWidth - addWidth - actionsWidth - columnGap * 2,
708
- 0,
709
- );
710
- if (compactWidth <= 0) {
711
- containerCacheRef.value = null;
712
- return null;
713
- }
714
-
715
- const cache = { compactWidth };
716
- containerCacheRef.value = cache;
717
- return cache;
718
- }
719
-
720
- function evaluateLayout() {
721
- if (props.mode !== "input") {
722
- updateLayout("compact");
723
- return;
724
- }
725
-
726
- if (
727
- typeof window.matchMedia === "function" &&
728
- window.matchMedia("(max-width: 767px)").matches
729
- ) {
730
- ensureMeasurements();
731
- adjustTextareaHeight();
732
- updateLayout("expanded");
733
- return;
734
- }
735
-
736
- const textarea = textareaRef.value;
737
- if (
738
- !textarea ||
739
- !gridRef.value ||
740
- !addButtonContainerRef.value ||
741
- !actionsContainerRef.value
742
- ) {
743
- return;
744
- }
745
-
746
- if (measurements.value.singleLineHeight === 0) {
747
- ensureMeasurements();
748
- }
749
-
750
- const scrollHeight = adjustTextareaHeight();
751
- const baseline = measurements.value.singleLineHeight;
752
- const hasExplicitBreak = inputValue.value.includes("\n");
753
- const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
754
- let shouldExpand = hasExplicitBreak || renderedMultiline;
755
-
756
- if (!shouldExpand) {
757
- const cache = containerCacheRef.value ?? updateContainerCache();
758
- if (cache && cache.compactWidth > 0) {
759
- const compactInnerWidth = Math.max(
760
- cache.compactWidth -
761
- measurements.value.paddingLeft -
762
- measurements.value.paddingRight,
763
- 0,
764
- );
765
-
766
- if (compactInnerWidth > 0) {
767
- const canvas =
768
- measurementCanvasRef.value ?? document.createElement("canvas");
769
- if (!measurementCanvasRef.value) {
770
- measurementCanvasRef.value = canvas;
771
- }
772
-
773
- const context = canvas.getContext("2d");
774
- if (context) {
775
- const resolvedFont = resolveTextareaFont(textarea);
776
- if (resolvedFont) {
777
- context.font = resolvedFont;
778
- const lines =
779
- inputValue.value.length > 0 ? inputValue.value.split("\n") : [""];
780
- let longestLine = 0;
781
- for (const line of lines) {
782
- const width = context.measureText(line || " ").width;
783
- if (width > longestLine) {
784
- longestLine = width;
785
- }
786
- }
787
- if (longestLine > compactInnerWidth) {
788
- shouldExpand = true;
789
- }
790
- }
791
- } else if (
792
- process.env.NODE_ENV !== "production" &&
793
- !didWarnMissingCanvasContextRef.value
794
- ) {
795
- didWarnMissingCanvasContextRef.value = true;
796
- console.warn(
797
- "[CopilotChatInput] canvas.getContext('2d') returned null. " +
798
- "Text-width-based expansion will be skipped.",
799
- );
800
- }
801
- }
802
- }
803
- }
804
-
805
- updateLayout(shouldExpand ? "expanded" : "compact");
806
- }
807
-
808
- function scheduleLayoutEvaluation(invalidateCache: boolean) {
809
- if (ignoreResizeRef.value) {
810
- ignoreResizeRef.value = false;
811
- return;
812
- }
813
-
814
- if (invalidateCache) {
815
- containerCacheRef.value = null;
816
- didWarnMissingFontRef.value = false;
817
- didWarnMissingCanvasContextRef.value = false;
818
- }
819
-
820
- if (resizeEvaluationRafRef.value !== null) {
821
- cancelAnimationFrame(resizeEvaluationRafRef.value);
822
- }
823
-
824
- resizeEvaluationRafRef.value = requestAnimationFrame(() => {
825
- resizeEvaluationRafRef.value = null;
826
- evaluateLayout();
827
- });
828
- }
829
-
830
416
  watch(
831
- () => props.modelValue,
832
- (next) => {
833
- if (isControlled.value) {
834
- localValue.value = next ?? "";
835
- }
417
+ [inputValue, () => props.mode],
418
+ ([value, mode]) => {
419
+ autosize.evaluate(value, mode);
836
420
  },
421
+ { flush: "post" },
837
422
  );
838
423
 
839
- watch(inputValue, (value) => {
840
- updateSlashState(value);
841
- evaluateLayout();
842
- });
843
-
844
- watch(commandItems, () => {
845
- if (commandItems.value.length === 0) {
846
- commandQuery.value = null;
847
- }
848
- });
849
-
850
424
  watch(
851
- [commandQuery, () => filteredCommands.value.length],
852
- ([query, filteredCount]) => {
853
- if (
854
- query !== null &&
855
- query !== previousCommandQuery.value &&
856
- filteredCount > 0
857
- ) {
858
- slashHighlightIndex.value = 0;
859
- }
860
-
861
- previousCommandQuery.value = query;
425
+ () => props.maxRows,
426
+ (maxRows) => {
427
+ autosize.setMaxRows(maxRows);
862
428
  },
863
- { immediate: true },
864
- );
865
-
866
- watch(
867
- [commandQuery, filteredCommands],
868
- () => {
869
- if (commandQuery.value === null) {
870
- slashHighlightIndex.value = 0;
871
- return;
872
- }
873
- if (filteredCommands.value.length === 0) {
874
- slashHighlightIndex.value = -1;
875
- return;
876
- }
877
- if (
878
- slashHighlightIndex.value < 0 ||
879
- slashHighlightIndex.value >= filteredCommands.value.length
880
- ) {
881
- slashHighlightIndex.value = 0;
882
- }
883
- },
884
- { immediate: true },
885
429
  );
886
430
 
887
431
  watch(
888
432
  () => props.mode,
889
- async (mode) => {
433
+ (mode) => {
890
434
  if (mode !== "input") {
891
- layout.value = "compact";
892
- commandQuery.value = null;
893
435
  closeAddMenu();
894
436
  }
895
-
896
- const recorder = audioRecorderRef.value;
897
- if (!recorder) {
898
- return;
899
- }
900
-
901
- if (mode === "transcribe") {
902
- try {
903
- await recorder.start();
904
- } catch (error) {
905
- console.error(error);
906
- }
907
- return;
908
- }
909
-
910
- if (recorder.state === "recording") {
911
- try {
912
- await recorder.stop();
913
- } catch {
914
- // ignore transition stop failures
915
- }
916
- }
917
437
  },
918
- { immediate: true },
919
438
  );
920
439
 
440
+ // Recording follows the mode: it starts in transcribe mode and stops when the
441
+ // mode changes. The recorder renders only in transcribe mode, so also sync
442
+ // when it appears.
921
443
  watch(
922
- audioRecorderRef,
923
- async (recorder) => {
924
- if (!recorder || props.mode !== "transcribe") {
925
- return;
926
- }
927
- if (recorder.state === "idle") {
928
- try {
929
- await recorder.start();
930
- } catch (error) {
931
- console.error(error);
932
- }
933
- }
444
+ [audioRecorderRef, () => props.mode],
445
+ ([recorder, mode]) => {
446
+ void ɵsyncRecorderToMode(recorder, mode);
934
447
  },
935
- { immediate: true },
448
+ { immediate: true, flush: "post" },
936
449
  );
937
450
 
938
451
  watch(slashHighlightIndex, async (index) => {
@@ -960,39 +473,11 @@ onMounted(() => {
960
473
  textareaRef.value?.focus();
961
474
  }
962
475
 
963
- evaluateLayout();
964
-
965
- if (typeof ResizeObserver !== "undefined") {
966
- const containerTargets = new Set<HTMLElement>();
967
- if (gridRef.value) containerTargets.add(gridRef.value);
968
- if (addButtonContainerRef.value)
969
- containerTargets.add(addButtonContainerRef.value);
970
- if (actionsContainerRef.value)
971
- containerTargets.add(actionsContainerRef.value);
972
-
973
- resizeObserver = new ResizeObserver((entries) => {
974
- const shouldInvalidate = entries.some((entry) =>
975
- containerTargets.has(entry.target as HTMLElement),
976
- );
977
- scheduleLayoutEvaluation(shouldInvalidate);
978
- });
979
-
980
- if (gridRef.value) resizeObserver.observe(gridRef.value);
981
- if (addButtonContainerRef.value)
982
- resizeObserver.observe(addButtonContainerRef.value);
983
- if (actionsContainerRef.value)
984
- resizeObserver.observe(actionsContainerRef.value);
985
- if (textareaRef.value) resizeObserver.observe(textareaRef.value);
986
- }
476
+ autosize.evaluate(inputValue.value, props.mode);
987
477
  });
988
478
 
989
479
  onBeforeUnmount(() => {
990
- resizeObserver?.disconnect();
991
- resizeObserver = null;
992
- if (resizeEvaluationRafRef.value !== null) {
993
- cancelAnimationFrame(resizeEvaluationRafRef.value);
994
- resizeEvaluationRafRef.value = null;
995
- }
480
+ autosize.dispose();
996
481
  });
997
482
  </script>
998
483
 
@@ -1174,7 +659,7 @@ onBeforeUnmount(() => {
1174
659
  index === slashHighlightIndex ? 'true' : undefined
1175
660
  "
1176
661
  class="cpk-chat-input-slash-menu-item"
1177
- @mouseenter="slashHighlightIndex = index"
662
+ @mouseenter="composer.highlight(index)"
1178
663
  @mousedown.prevent="runCommand(command)"
1179
664
  >
1180
665
  {{ command.label }}
@@ -1295,7 +780,7 @@ onBeforeUnmount(() => {
1295
780
  type="button"
1296
781
  data-testid="copilot-chat-input-send"
1297
782
  :aria-label="
1298
- isProcessing && hasStopAction
783
+ showsStop
1299
784
  ? labels.chatInputToolbarStopButtonLabel
1300
785
  : labels.chatInputToolbarSendButtonLabel
1301
786
  "
@@ -1304,7 +789,7 @@ onBeforeUnmount(() => {
1304
789
  @click="handleSendButtonClick"
1305
790
  >
1306
791
  <IconSquare
1307
- v-if="isProcessing && hasStopAction"
792
+ v-if="showsStop"
1308
793
  class="cpk-chat-input-stop-icon"
1309
794
  />
1310
795
  <IconArrowUp v-else class="cpk-chat-input-send-icon" />