@copilotkit/vue 2.0.0-rc.20 → 2.0.0-rc.21

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 (193) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.mjs +13 -13
  3. package/dist/use-render-activity-message-BAhbpSDh.js +16200 -0
  4. package/dist/use-render-activity-message-BAhbpSDh.js.map +1 -0
  5. package/dist/use-render-activity-message-DJ6q1qHV.cjs +114 -0
  6. package/dist/use-render-activity-message-DJ6q1qHV.cjs.map +1 -0
  7. package/dist/v2/components/A2UIMessageRenderer.d.ts +14 -0
  8. package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
  9. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts +10 -3
  10. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
  11. package/dist/v2/components/CopilotKitInspector.vue.d.ts +5 -1
  12. package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -1
  13. package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
  14. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +7 -21
  15. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -1
  16. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +0 -5
  17. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
  18. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +10 -15
  19. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -1
  20. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +14 -138
  21. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -1
  22. package/dist/v2/components/a2ui/catalog.d.ts.map +1 -1
  23. package/dist/v2/components/a2ui/utils.d.ts +3 -15
  24. package/dist/v2/components/a2ui/utils.d.ts.map +1 -1
  25. package/dist/v2/components/a2ui.d.ts +6 -1
  26. package/dist/v2/components/a2ui.d.ts.map +1 -1
  27. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  28. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  29. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts +4 -8
  30. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
  31. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  32. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -1
  33. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  34. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  35. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  36. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  37. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -1
  38. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  39. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  40. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +1 -1
  41. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +1 -1
  42. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  43. package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts.map +1 -1
  44. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts +1 -0
  45. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
  46. package/dist/v2/components/chat/audioRecorder.d.ts +3 -4
  47. package/dist/v2/components/chat/audioRecorder.d.ts.map +1 -1
  48. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +3 -2
  49. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  50. package/dist/v2/components/chat/index.d.ts +6 -6
  51. package/dist/v2/components/chat/tool-call-display-context.d.ts +3 -1
  52. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  53. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  54. package/dist/v2/components/chat/types.d.ts +1 -1
  55. package/dist/v2/components/chat/types.d.ts.map +1 -1
  56. package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -1
  57. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +1 -1
  58. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -1
  59. package/dist/v2/hooks/use-agent-context.d.ts +2 -3
  60. package/dist/v2/hooks/use-agent-context.d.ts.map +1 -1
  61. package/dist/v2/hooks/use-agent.d.ts +4 -1
  62. package/dist/v2/hooks/use-agent.d.ts.map +1 -1
  63. package/dist/v2/hooks/use-attachments.d.ts +7 -0
  64. package/dist/v2/hooks/use-attachments.d.ts.map +1 -1
  65. package/dist/v2/hooks/use-capabilities.d.ts.map +1 -1
  66. package/dist/v2/hooks/use-component.d.ts +1 -0
  67. package/dist/v2/hooks/use-component.d.ts.map +1 -1
  68. package/dist/v2/hooks/use-configure-suggestions.d.ts +4 -9
  69. package/dist/v2/hooks/use-configure-suggestions.d.ts.map +1 -1
  70. package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
  71. package/dist/v2/hooks/use-human-in-the-loop.d.ts +2 -1
  72. package/dist/v2/hooks/use-human-in-the-loop.d.ts.map +1 -1
  73. package/dist/v2/hooks/use-interrupt.d.ts +0 -1
  74. package/dist/v2/hooks/use-interrupt.d.ts.map +1 -1
  75. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  76. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  77. package/dist/v2/hooks/use-suggestions.d.ts.map +1 -1
  78. package/dist/v2/hooks/use-threads.d.ts +0 -8
  79. package/dist/v2/hooks/use-threads.d.ts.map +1 -1
  80. package/dist/v2/index.cjs +1 -1
  81. package/dist/v2/index.mjs +1 -1
  82. package/dist/v2/lib/vue-core.d.ts +12 -11
  83. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  84. package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
  85. package/dist/v2/providers/CopilotKitProvider.types.d.ts +9 -124
  86. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  87. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +9 -10
  88. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  89. package/dist/v2/providers/chat-configuration-store-key.d.ts +8 -0
  90. package/dist/v2/providers/chat-configuration-store-key.d.ts.map +1 -0
  91. package/dist/v2/providers/markdown-renderer.d.ts +3 -1
  92. package/dist/v2/providers/markdown-renderer.d.ts.map +1 -1
  93. package/dist/v2/providers/use-inspector-thread-override.d.ts +5 -0
  94. package/dist/v2/providers/use-inspector-thread-override.d.ts.map +1 -1
  95. package/package.json +8 -8
  96. package/src/v2/__tests__/exports.test.ts +10 -0
  97. package/src/v2/__tests__/mocks/web-inspector.ts +6 -0
  98. package/src/v2/__tests__/utils/test-helpers.ts +34 -4
  99. package/src/v2/components/A2UIMessageRenderer.ts +98 -68
  100. package/src/v2/components/A2UISurfaceActivityRenderer.vue +68 -103
  101. package/src/v2/components/CopilotKitInspector.vue +104 -51
  102. package/src/v2/components/MCPAppsActivityRenderer.ts +75 -201
  103. package/src/v2/components/OpenGenerativeUIRenderer.ts +78 -80
  104. package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +171 -8
  105. package/src/v2/components/__tests__/CopilotKitInspector.test.ts +42 -0
  106. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +9 -2
  107. package/src/v2/components/__tests__/OpenGenerativeUIToolRenderer.test.ts +38 -0
  108. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +1 -6
  109. package/src/v2/components/a2ui/A2UICatalogContext.ts +27 -93
  110. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +127 -327
  111. package/src/v2/components/a2ui/catalog.ts +2 -3
  112. package/src/v2/components/a2ui/utils.ts +13 -67
  113. package/src/v2/components/a2ui.ts +14 -21
  114. package/src/v2/components/chat/CopilotChat.vue +136 -324
  115. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +22 -45
  116. package/src/v2/components/chat/CopilotChatAudioRecorder.vue +19 -246
  117. package/src/v2/components/chat/CopilotChatInput.vue +69 -407
  118. package/src/v2/components/chat/CopilotChatLightbox.vue +10 -31
  119. package/src/v2/components/chat/CopilotChatMessageView.vue +52 -112
  120. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +14 -45
  121. package/src/v2/components/chat/CopilotChatToolCallItem.vue +6 -4
  122. package/src/v2/components/chat/CopilotChatToolCallsView.vue +16 -19
  123. package/src/v2/components/chat/CopilotChatView.vue +28 -23
  124. package/src/v2/components/chat/CopilotPopupViewInternal.vue +21 -97
  125. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -25
  126. package/src/v2/components/chat/CopilotThreadsDrawer.vue +37 -102
  127. package/src/v2/components/chat/StreamingMarkdownDefault.vue +13 -26
  128. package/src/v2/components/chat/__tests__/CopilotChat.test.ts +12 -10
  129. package/src/v2/components/chat/__tests__/CopilotChatAttachmentQueue.test.ts +28 -0
  130. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +56 -0
  131. package/src/v2/components/chat/__tests__/CopilotPopupView.test.ts +19 -0
  132. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +32 -8
  133. package/src/v2/components/chat/__tests__/CopilotSidebarView.test.ts +2 -1
  134. package/src/v2/components/chat/__tests__/StreamingMarkdownDefault.test.ts +54 -0
  135. package/src/v2/components/chat/audioRecorder.ts +4 -7
  136. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +22 -71
  137. package/src/v2/components/chat/tool-call-display-context.ts +6 -1
  138. package/src/v2/components/chat/tool-call-group-parts.ts +13 -33
  139. package/src/v2/components/chat/types.ts +1 -1
  140. package/src/v2/components/chat/use-lightbox.ts +15 -33
  141. package/src/v2/hooks/__tests__/use-agent-error-state.test.ts +5 -3
  142. package/src/v2/hooks/__tests__/use-agent-stability.test.ts +25 -4
  143. package/src/v2/hooks/__tests__/use-agent-throttle.test.ts +5 -1
  144. package/src/v2/hooks/__tests__/use-agent.test.ts +6 -3
  145. package/src/v2/hooks/__tests__/use-component.test.ts +21 -0
  146. package/src/v2/hooks/__tests__/use-configure-suggestions.test.ts +30 -1
  147. package/src/v2/hooks/__tests__/use-default-render-tool.test.ts +10 -10
  148. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +4 -0
  149. package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.ts +6 -1
  150. package/src/v2/hooks/__tests__/use-human-in-the-loop.test.ts +187 -12
  151. package/src/v2/hooks/__tests__/use-interrupt.test.ts +97 -0
  152. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +2 -1
  153. package/src/v2/hooks/__tests__/use-threads.test.ts +48 -6
  154. package/src/v2/hooks/use-agent-context.ts +4 -10
  155. package/src/v2/hooks/use-agent.ts +86 -290
  156. package/src/v2/hooks/use-attachments.ts +42 -166
  157. package/src/v2/hooks/use-capabilities.ts +2 -7
  158. package/src/v2/hooks/use-component.ts +7 -4
  159. package/src/v2/hooks/use-configure-suggestions.ts +28 -218
  160. package/src/v2/hooks/use-default-render-tool.ts +34 -94
  161. package/src/v2/hooks/use-human-in-the-loop.ts +11 -80
  162. package/src/v2/hooks/use-interrupt.ts +29 -308
  163. package/src/v2/hooks/use-render-activity-message.ts +17 -20
  164. package/src/v2/hooks/use-render-custom-messages.ts +20 -63
  165. package/src/v2/hooks/use-suggestions.ts +9 -33
  166. package/src/v2/hooks/use-threads.ts +43 -273
  167. package/src/v2/lib/__tests__/vue-core-renderers.test.ts +42 -0
  168. package/src/v2/lib/vue-core.ts +67 -72
  169. package/src/v2/providers/CopilotChatConfigurationProvider.vue +48 -186
  170. package/src/v2/providers/CopilotKitProvider.types.ts +12 -127
  171. package/src/v2/providers/CopilotKitProvider.vue +188 -402
  172. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.ts +22 -2
  173. package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +19 -1
  174. package/src/v2/providers/__tests__/CopilotKitProvider.config.test.ts +124 -0
  175. package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.ts +4 -1
  176. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +45 -2
  177. package/src/v2/providers/chat-configuration-store-key.ts +9 -0
  178. package/src/v2/providers/markdown-renderer.ts +7 -1
  179. package/src/v2/providers/use-inspector-thread-override.ts +22 -80
  180. package/vite.config.ts +5 -1
  181. package/vitest.config.ts +12 -7
  182. package/dist/use-render-activity-message-CsWYyd6r.cjs +0 -91
  183. package/dist/use-render-activity-message-CsWYyd6r.cjs.map +0 -1
  184. package/dist/use-render-activity-message-DntKKNdd.js +0 -17418
  185. package/dist/use-render-activity-message-DntKKNdd.js.map +0 -1
  186. package/dist/v2/components/chat/normalize-auto-scroll.d.ts +0 -3
  187. package/dist/v2/components/chat/normalize-auto-scroll.d.ts.map +0 -1
  188. package/dist/v2/lib/is-mobile-viewport.d.ts +0 -2
  189. package/dist/v2/lib/is-mobile-viewport.d.ts.map +0 -1
  190. package/src/v2/components/__tests__/A2UICatalogContext.test.ts +0 -63
  191. package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +0 -175
  192. package/src/v2/components/chat/normalize-auto-scroll.ts +0 -3
  193. package/src/v2/lib/is-mobile-viewport.ts +0 -5
@@ -5,10 +5,20 @@ import {
5
5
  onBeforeUnmount,
6
6
  onMounted,
7
7
  ref,
8
+ shallowRef,
8
9
  useAttrs,
9
10
  watch,
10
11
  } from "vue";
11
- import { ɵcomposerButtonState, ɵresolveComposerKey } from "@copilotkit/core";
12
+ import {
13
+ ɵclampSlashIndex,
14
+ ɵcomposerButtonState,
15
+ ɵcreateComposerAutosize,
16
+ ɵfilterCommands,
17
+ ɵflattenCommandItems,
18
+ ɵparseSlashQuery,
19
+ ɵresolveComposerKey,
20
+ ɵresolveSlashKey,
21
+ } from "@copilotkit/core";
12
22
  import {
13
23
  IconArrowUp,
14
24
  IconCheck,
@@ -116,7 +126,20 @@ const slashMenuRef = ref<HTMLElement | null>(null);
116
126
  const audioRecorderRef = ref<CopilotChatAudioRecorderRef | null>(null);
117
127
  const localValue = ref(props.modelValue ?? "");
118
128
  const isComposing = ref(false);
119
- const layout = ref<"compact" | "expanded">("compact");
129
+ // Sizes the text box and picks the compact or expanded layout.
130
+ const autosize = ɵcreateComposerAutosize({
131
+ getElements: () => ({
132
+ textarea: textareaRef.value,
133
+ grid: gridRef.value,
134
+ start: addButtonContainerRef.value,
135
+ actions: actionsContainerRef.value,
136
+ }),
137
+ maxRows: props.maxRows,
138
+ });
139
+ const layout = shallowRef(autosize.getSnapshot());
140
+ autosize.subscribe(() => {
141
+ layout.value = autosize.getSnapshot();
142
+ });
120
143
  const commandQuery = ref<string | null>(null);
121
144
  const slashHighlightIndex = ref(0);
122
145
  const previousCommandQuery = ref<string | null>(null);
@@ -127,21 +150,6 @@ const addMenuInitialFocus = ref<"first" | "last" | "content">("content");
127
150
  const addMenuAnchor = ref<HTMLElement | null>(null);
128
151
  const addMenuId = useCopilotId("cpk-add-menu");
129
152
  const addMenuTriggerId = `${addMenuId}-trigger`;
130
- const measurements = ref({
131
- singleLineHeight: 0,
132
- maxHeight: 0,
133
- paddingLeft: 0,
134
- paddingRight: 0,
135
- });
136
- const resizeEvaluationRafRef = ref<number | null>(null);
137
- const ignoreResizeRef = ref(false);
138
- const measurementCanvasRef = ref<HTMLCanvasElement | null>(null);
139
- const containerCacheRef = ref<{
140
- compactWidth: number;
141
- } | null>(null);
142
- const didWarnMissingFontRef = ref(false);
143
- const didWarnMissingCanvasContextRef = ref(false);
144
- let resizeObserver: ResizeObserver | null = null;
145
153
 
146
154
  const isControlled = computed(() => props.modelValue !== undefined);
147
155
  const inputValue = computed(() =>
@@ -251,35 +259,7 @@ function normalizeMenuItems() {
251
259
  const menuItems = computed(() => normalizeMenuItems());
252
260
  const hasMenuItems = computed(() => menuItems.value.length > 0);
253
261
 
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));
262
+ const commandItems = computed(() => ɵflattenCommandItems(menuItems.value));
283
263
 
284
264
  function flattenMenuDisplay(items: MenuEntry[], depth = 0, prefix = "root") {
285
265
  const entries: MenuDisplayEntry[] = [];
@@ -314,32 +294,9 @@ function flattenMenuDisplay(items: MenuEntry[], depth = 0, prefix = "root") {
314
294
 
315
295
  const menuDisplayItems = computed(() => flattenMenuDisplay(menuItems.value));
316
296
 
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
- });
297
+ const filteredCommands = computed(() =>
298
+ ɵfilterCommands(commandItems.value, commandQuery.value),
299
+ );
343
300
 
344
301
  const slashMenuVisible = computed(
345
302
  () => commandQuery.value !== null && commandItems.value.length > 0,
@@ -357,18 +314,7 @@ function clearInputValue() {
357
314
  }
358
315
 
359
316
  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;
317
+ commandQuery.value = ɵparseSlashQuery(value, commandItems.value.length > 0);
372
318
  }
373
319
 
374
320
  function runCommand(command: ToolsMenuItem) {
@@ -429,50 +375,24 @@ function handleKeydown(event: KeyboardEvent) {
429
375
  return;
430
376
  }
431
377
 
432
- if (commandQuery.value !== null && props.mode === "input") {
433
- if (event.key === "ArrowDown") {
434
- if (filteredCommands.value.length > 0) {
435
- event.preventDefault();
436
- slashHighlightIndex.value =
437
- slashHighlightIndex.value < 0
438
- ? 0
439
- : (slashHighlightIndex.value + 1) % filteredCommands.value.length;
440
- }
378
+ const slash = ɵresolveSlashKey(event, {
379
+ query: props.mode === "input" ? commandQuery.value : null,
380
+ commands: filteredCommands.value,
381
+ highlightIndex: slashHighlightIndex.value,
382
+ });
383
+ if (slash.type !== "none") event.preventDefault();
384
+ switch (slash.type) {
385
+ case "highlight":
386
+ slashHighlightIndex.value = slash.index;
441
387
  return;
442
- }
443
-
444
- if (event.key === "ArrowUp") {
445
- if (filteredCommands.value.length > 0) {
446
- event.preventDefault();
447
- if (slashHighlightIndex.value < 0) {
448
- slashHighlightIndex.value = filteredCommands.value.length - 1;
449
- } else {
450
- slashHighlightIndex.value =
451
- slashHighlightIndex.value <= 0
452
- ? filteredCommands.value.length - 1
453
- : slashHighlightIndex.value - 1;
454
- }
455
- }
388
+ case "run":
389
+ runCommand(slash.command);
456
390
  return;
457
- }
458
-
459
- if (event.key === "Enter") {
460
- const selected =
461
- slashHighlightIndex.value >= 0
462
- ? filteredCommands.value[slashHighlightIndex.value]
463
- : undefined;
464
- if (selected) {
465
- event.preventDefault();
466
- runCommand(selected);
467
- return;
468
- }
469
- }
470
-
471
- if (event.key === "Escape") {
472
- event.preventDefault();
391
+ case "close":
473
392
  commandQuery.value = null;
474
393
  return;
475
- }
394
+ case "none":
395
+ break;
476
396
  }
477
397
 
478
398
  if (props.mode !== "input") {
@@ -603,240 +523,6 @@ function handleContainerClick(event: MouseEvent) {
603
523
  textareaRef.value?.focus();
604
524
  }
605
525
 
606
- function ensureMeasurements() {
607
- const textarea = textareaRef.value;
608
- if (!textarea || isComposing.value) {
609
- return;
610
- }
611
-
612
- const previousValue = textarea.value;
613
- const previousHeight = textarea.style.height;
614
- textarea.style.height = "auto";
615
-
616
- const computedStyle = window.getComputedStyle(textarea);
617
- const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
618
- const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
619
- const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
620
- const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
621
-
622
- textarea.value = "";
623
- const singleLineHeight = textarea.scrollHeight;
624
- textarea.value = previousValue;
625
-
626
- const contentHeight = singleLineHeight - paddingTop - paddingBottom;
627
- const maxHeight = contentHeight * props.maxRows + paddingTop + paddingBottom;
628
-
629
- measurements.value = {
630
- singleLineHeight,
631
- maxHeight,
632
- paddingLeft,
633
- paddingRight,
634
- };
635
-
636
- textarea.style.height = previousHeight;
637
- textarea.style.maxHeight = `${maxHeight}px`;
638
- }
639
-
640
- function adjustTextareaHeight() {
641
- const textarea = textareaRef.value;
642
- if (!textarea) {
643
- return 0;
644
- }
645
-
646
- if (measurements.value.singleLineHeight === 0) {
647
- ensureMeasurements();
648
- }
649
-
650
- const { maxHeight } = measurements.value;
651
- if (maxHeight) {
652
- textarea.style.maxHeight = `${maxHeight}px`;
653
- }
654
-
655
- textarea.style.height = "auto";
656
- const scrollHeight = textarea.scrollHeight;
657
- textarea.style.height = `${maxHeight ? Math.min(scrollHeight, maxHeight) : scrollHeight}px`;
658
- return scrollHeight;
659
- }
660
-
661
- function updateLayout(nextLayout: "compact" | "expanded") {
662
- if (layout.value === nextLayout) {
663
- return;
664
- }
665
- ignoreResizeRef.value = true;
666
- layout.value = nextLayout;
667
- }
668
-
669
- function resolveTextareaFont(textarea: HTMLTextAreaElement): string | null {
670
- const textareaStyles = window.getComputedStyle(textarea);
671
- if (textareaStyles.font?.trim()) {
672
- return textareaStyles.font;
673
- }
674
-
675
- if (textareaStyles.fontSize && textareaStyles.fontFamily) {
676
- const fallbackFont =
677
- `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ` +
678
- `${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ` +
679
- `${textareaStyles.fontFamily}`;
680
- if (fallbackFont.trim()) {
681
- return fallbackFont;
682
- }
683
- }
684
-
685
- if (process.env.NODE_ENV !== "production" && !didWarnMissingFontRef.value) {
686
- didWarnMissingFontRef.value = true;
687
- console.warn(
688
- "[CopilotChatInput] Could not resolve textarea font for layout measurement. " +
689
- "Text-width-based expansion will be skipped until the next container resize.",
690
- );
691
- }
692
- return null;
693
- }
694
-
695
- function updateContainerCache() {
696
- const grid = gridRef.value;
697
- const addContainer = addButtonContainerRef.value;
698
- const actionsContainer = actionsContainerRef.value;
699
- if (!grid || !addContainer || !actionsContainer) {
700
- containerCacheRef.value = null;
701
- return null;
702
- }
703
-
704
- const gridStyles = window.getComputedStyle(grid);
705
- const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
706
- const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
707
- const columnGap = parseFloat(gridStyles.columnGap) || 0;
708
- const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
709
- if (gridAvailableWidth <= 0) {
710
- containerCacheRef.value = null;
711
- return null;
712
- }
713
-
714
- const addWidth = addContainer.getBoundingClientRect().width;
715
- const actionsWidth = actionsContainer.getBoundingClientRect().width;
716
- const compactWidth = Math.max(
717
- gridAvailableWidth - addWidth - actionsWidth - columnGap * 2,
718
- 0,
719
- );
720
- if (compactWidth <= 0) {
721
- containerCacheRef.value = null;
722
- return null;
723
- }
724
-
725
- const cache = { compactWidth };
726
- containerCacheRef.value = cache;
727
- return cache;
728
- }
729
-
730
- function evaluateLayout() {
731
- if (props.mode !== "input") {
732
- updateLayout("compact");
733
- return;
734
- }
735
-
736
- if (
737
- typeof window.matchMedia === "function" &&
738
- window.matchMedia("(max-width: 767px)").matches
739
- ) {
740
- ensureMeasurements();
741
- adjustTextareaHeight();
742
- updateLayout("expanded");
743
- return;
744
- }
745
-
746
- const textarea = textareaRef.value;
747
- if (
748
- !textarea ||
749
- !gridRef.value ||
750
- !addButtonContainerRef.value ||
751
- !actionsContainerRef.value
752
- ) {
753
- return;
754
- }
755
-
756
- if (measurements.value.singleLineHeight === 0) {
757
- ensureMeasurements();
758
- }
759
-
760
- const scrollHeight = adjustTextareaHeight();
761
- const baseline = measurements.value.singleLineHeight;
762
- const hasExplicitBreak = inputValue.value.includes("\n");
763
- const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
764
- let shouldExpand = hasExplicitBreak || renderedMultiline;
765
-
766
- if (!shouldExpand) {
767
- const cache = containerCacheRef.value ?? updateContainerCache();
768
- if (cache && cache.compactWidth > 0) {
769
- const compactInnerWidth = Math.max(
770
- cache.compactWidth -
771
- measurements.value.paddingLeft -
772
- measurements.value.paddingRight,
773
- 0,
774
- );
775
-
776
- if (compactInnerWidth > 0) {
777
- const canvas =
778
- measurementCanvasRef.value ?? document.createElement("canvas");
779
- if (!measurementCanvasRef.value) {
780
- measurementCanvasRef.value = canvas;
781
- }
782
-
783
- const context = canvas.getContext("2d");
784
- if (context) {
785
- const resolvedFont = resolveTextareaFont(textarea);
786
- if (resolvedFont) {
787
- context.font = resolvedFont;
788
- const lines =
789
- inputValue.value.length > 0 ? inputValue.value.split("\n") : [""];
790
- let longestLine = 0;
791
- for (const line of lines) {
792
- const width = context.measureText(line || " ").width;
793
- if (width > longestLine) {
794
- longestLine = width;
795
- }
796
- }
797
- if (longestLine > compactInnerWidth) {
798
- shouldExpand = true;
799
- }
800
- }
801
- } else if (
802
- process.env.NODE_ENV !== "production" &&
803
- !didWarnMissingCanvasContextRef.value
804
- ) {
805
- didWarnMissingCanvasContextRef.value = true;
806
- console.warn(
807
- "[CopilotChatInput] canvas.getContext('2d') returned null. " +
808
- "Text-width-based expansion will be skipped.",
809
- );
810
- }
811
- }
812
- }
813
- }
814
-
815
- updateLayout(shouldExpand ? "expanded" : "compact");
816
- }
817
-
818
- function scheduleLayoutEvaluation(invalidateCache: boolean) {
819
- if (ignoreResizeRef.value) {
820
- ignoreResizeRef.value = false;
821
- return;
822
- }
823
-
824
- if (invalidateCache) {
825
- containerCacheRef.value = null;
826
- didWarnMissingFontRef.value = false;
827
- didWarnMissingCanvasContextRef.value = false;
828
- }
829
-
830
- if (resizeEvaluationRafRef.value !== null) {
831
- cancelAnimationFrame(resizeEvaluationRafRef.value);
832
- }
833
-
834
- resizeEvaluationRafRef.value = requestAnimationFrame(() => {
835
- resizeEvaluationRafRef.value = null;
836
- evaluateLayout();
837
- });
838
- }
839
-
840
526
  watch(
841
527
  () => props.modelValue,
842
528
  (next) => {
@@ -848,13 +534,25 @@ watch(
848
534
 
849
535
  watch(inputValue, (value) => {
850
536
  updateSlashState(value);
851
- evaluateLayout();
852
537
  });
853
538
 
539
+ watch(
540
+ [inputValue, () => props.mode],
541
+ ([value, mode]) => {
542
+ autosize.evaluate(value, mode);
543
+ },
544
+ { flush: "post" },
545
+ );
546
+
547
+ watch(
548
+ () => props.maxRows,
549
+ (maxRows) => {
550
+ autosize.setMaxRows(maxRows);
551
+ },
552
+ );
553
+
854
554
  watch(commandItems, () => {
855
- if (commandItems.value.length === 0) {
856
- commandQuery.value = null;
857
- }
555
+ updateSlashState(inputValue.value);
858
556
  });
859
557
 
860
558
  watch(
@@ -876,20 +574,13 @@ watch(
876
574
  watch(
877
575
  [commandQuery, filteredCommands],
878
576
  () => {
879
- if (commandQuery.value === null) {
880
- slashHighlightIndex.value = 0;
881
- return;
882
- }
883
- if (filteredCommands.value.length === 0) {
884
- slashHighlightIndex.value = -1;
885
- return;
886
- }
887
- if (
888
- slashHighlightIndex.value < 0 ||
889
- slashHighlightIndex.value >= filteredCommands.value.length
890
- ) {
891
- slashHighlightIndex.value = 0;
892
- }
577
+ slashHighlightIndex.value =
578
+ commandQuery.value === null
579
+ ? 0
580
+ : ɵclampSlashIndex(
581
+ slashHighlightIndex.value,
582
+ filteredCommands.value.length,
583
+ );
893
584
  },
894
585
  { immediate: true },
895
586
  );
@@ -898,7 +589,6 @@ watch(
898
589
  () => props.mode,
899
590
  async (mode) => {
900
591
  if (mode !== "input") {
901
- layout.value = "compact";
902
592
  commandQuery.value = null;
903
593
  closeAddMenu();
904
594
  }
@@ -970,39 +660,11 @@ onMounted(() => {
970
660
  textareaRef.value?.focus();
971
661
  }
972
662
 
973
- evaluateLayout();
974
-
975
- if (typeof ResizeObserver !== "undefined") {
976
- const containerTargets = new Set<HTMLElement>();
977
- if (gridRef.value) containerTargets.add(gridRef.value);
978
- if (addButtonContainerRef.value)
979
- containerTargets.add(addButtonContainerRef.value);
980
- if (actionsContainerRef.value)
981
- containerTargets.add(actionsContainerRef.value);
982
-
983
- resizeObserver = new ResizeObserver((entries) => {
984
- const shouldInvalidate = entries.some((entry) =>
985
- containerTargets.has(entry.target as HTMLElement),
986
- );
987
- scheduleLayoutEvaluation(shouldInvalidate);
988
- });
989
-
990
- if (gridRef.value) resizeObserver.observe(gridRef.value);
991
- if (addButtonContainerRef.value)
992
- resizeObserver.observe(addButtonContainerRef.value);
993
- if (actionsContainerRef.value)
994
- resizeObserver.observe(actionsContainerRef.value);
995
- if (textareaRef.value) resizeObserver.observe(textareaRef.value);
996
- }
663
+ autosize.evaluate(inputValue.value, props.mode);
997
664
  });
998
665
 
999
666
  onBeforeUnmount(() => {
1000
- resizeObserver?.disconnect();
1001
- resizeObserver = null;
1002
- if (resizeEvaluationRafRef.value !== null) {
1003
- cancelAnimationFrame(resizeEvaluationRafRef.value);
1004
- resizeEvaluationRafRef.value = null;
1005
- }
667
+ autosize.dispose();
1006
668
  });
1007
669
  </script>
1008
670
 
@@ -9,6 +9,7 @@
9
9
  * the element that opened it (or `returnFocusTo`) when it closes.
10
10
  */
11
11
  import { computed, nextTick, onBeforeUnmount, onMounted, ref } from "vue";
12
+ import { ɵbindDialogFocus } from "@copilotkit/core";
12
13
  import { IconX } from "../icons";
13
14
  import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
14
15
  import { CopilotChatDefaultLabels } from "../../providers/types";
@@ -29,49 +30,28 @@ const labels = computed(() => config.value?.labels ?? CopilotChatDefaultLabels);
29
30
 
30
31
  const root = ref<HTMLElement | null>(null);
31
32
  const closeButton = ref<HTMLButtonElement | null>(null);
32
- let previouslyFocused: HTMLElement | null = null;
33
-
34
- const FOCUSABLE =
35
- 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), audio[controls], video[controls], iframe, [tabindex]:not([tabindex="-1"])';
33
+ let releaseFocus: (() => void) | undefined;
36
34
 
37
35
  function handleKeydown(event: KeyboardEvent) {
38
36
  if (event.key === "Escape") emit("close");
39
37
  }
40
38
 
41
- /** Keeps Tab inside the lightbox while it is open. */
42
- function handleTab(event: KeyboardEvent) {
43
- if (event.key !== "Tab" || !root.value) return;
44
- const focusables = Array.from(
45
- root.value.querySelectorAll<HTMLElement>(FOCUSABLE),
46
- );
47
- const first = focusables[0];
48
- const last = focusables[focusables.length - 1];
49
- if (!first || !last) return;
50
- const active = document.activeElement;
51
- if (event.shiftKey && (active === first || !root.value.contains(active))) {
52
- event.preventDefault();
53
- last.focus();
54
- } else if (!event.shiftKey && active === last) {
55
- event.preventDefault();
56
- first.focus();
57
- }
58
- }
59
-
60
39
  onMounted(() => {
61
40
  if (typeof document === "undefined") return;
62
- previouslyFocused =
63
- document.activeElement instanceof HTMLElement
64
- ? document.activeElement
65
- : null;
66
41
  document.addEventListener("keydown", handleKeydown);
67
- void nextTick(() => closeButton.value?.focus());
42
+ void nextTick(() => {
43
+ if (!root.value) return;
44
+ releaseFocus = ɵbindDialogFocus(root.value, {
45
+ initialFocus: closeButton.value,
46
+ returnFocusTo: props.returnFocusTo,
47
+ });
48
+ });
68
49
  });
69
50
 
70
51
  onBeforeUnmount(() => {
71
52
  if (typeof document === "undefined") return;
72
53
  document.removeEventListener("keydown", handleKeydown);
73
- const target = props.returnFocusTo ?? previouslyFocused;
74
- if (target?.isConnected) target.focus();
54
+ releaseFocus?.();
75
55
  });
76
56
  </script>
77
57
 
@@ -85,7 +65,6 @@ onBeforeUnmount(() => {
85
65
  :aria-label="label"
86
66
  data-testid="copilot-chat-attachment-lightbox"
87
67
  @click="emit('close')"
88
- @keydown="handleTab"
89
68
  >
90
69
  <button
91
70
  ref="closeButton"