@copilotkit/vue 2.0.0-rc.7 → 2.0.0-rc.9

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 (156) hide show
  1. package/dist/hooks/use-copilot-action.d.ts.map +1 -1
  2. package/dist/hooks/use-frontend-tool.d.ts +1 -1
  3. package/dist/hooks/use-frontend-tool.d.ts.map +1 -1
  4. package/dist/hooks/zod-parameters.d.ts +14 -0
  5. package/dist/hooks/zod-parameters.d.ts.map +1 -0
  6. package/dist/index.cjs +1 -73
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.mjs +353 -16906
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/use-render-activity-message-B59A4R48.js +18097 -0
  11. package/dist/use-render-activity-message-B59A4R48.js.map +1 -0
  12. package/dist/use-render-activity-message-u0OVXjd8.cjs +92 -0
  13. package/dist/use-render-activity-message-u0OVXjd8.cjs.map +1 -0
  14. package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -1
  15. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +13 -45
  16. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -1
  17. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +1 -1
  18. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -1
  19. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +13 -5
  20. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -1
  21. package/dist/v2/components/a2ui.d.ts +3 -7
  22. package/dist/v2/components/a2ui.d.ts.map +1 -1
  23. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  24. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  25. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  26. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +6 -270
  27. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  28. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +1 -1
  29. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  30. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  31. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +1 -1
  32. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  33. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  34. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +1 -1
  35. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  36. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  37. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -1
  38. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +0 -1
  39. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  40. package/dist/v2/components/chat/index.d.ts +0 -1
  41. package/dist/v2/components/chat/index.d.ts.map +1 -1
  42. package/dist/v2/components/chat/tool-call-display-context.d.ts +2 -19
  43. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  44. package/dist/v2/components/chat/tool-call-group-parts.d.ts +7 -20
  45. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  46. package/dist/v2/components/chat/turn-work.d.ts +0 -2
  47. package/dist/v2/components/chat/turn-work.d.ts.map +1 -1
  48. package/dist/v2/components/chat/types.d.ts +1 -2
  49. package/dist/v2/components/chat/types.d.ts.map +1 -1
  50. package/dist/v2/components/chat/use-chat-labels.d.ts +8 -0
  51. package/dist/v2/components/chat/use-chat-labels.d.ts.map +1 -0
  52. package/dist/v2/components/icons/index.d.ts +968 -1
  53. package/dist/v2/components/icons/index.d.ts.map +1 -1
  54. package/dist/v2/hooks/use-frontend-tool.d.ts +5 -0
  55. package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
  56. package/dist/v2/hooks/use-render-activity-message.d.ts +13 -9
  57. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  58. package/dist/v2/hooks/use-render-custom-messages.d.ts +5 -7
  59. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  60. package/dist/v2/index.cjs +1 -1
  61. package/dist/v2/index.mjs +197 -200
  62. package/dist/v2/index.mjs.map +1 -1
  63. package/dist/v2/lib/processPartialHtml.d.ts +1 -2
  64. package/dist/v2/lib/processPartialHtml.d.ts.map +1 -1
  65. package/dist/v2/lib/vue-core.d.ts +0 -1
  66. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  67. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +1 -1
  68. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  69. package/dist/v2/providers/types.d.ts +3 -46
  70. package/dist/v2/providers/types.d.ts.map +1 -1
  71. package/dist/v2/types/human-in-the-loop.d.ts +8 -29
  72. package/dist/v2/types/human-in-the-loop.d.ts.map +1 -1
  73. package/dist/v2/types/interrupt.d.ts +1 -22
  74. package/dist/v2/types/interrupt.d.ts.map +1 -1
  75. package/dist/v2/types/vue-activity-message-renderer.d.ts +16 -15
  76. package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -1
  77. package/dist/v2/types/vue-custom-message-renderer.d.ts +7 -16
  78. package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -1
  79. package/dist/v2/types/vue-tool-call-renderer.d.ts +16 -35
  80. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  81. package/package.json +10 -11
  82. package/src/__tests__/build-entries.test.ts +27 -0
  83. package/src/hooks/__tests__/zod-parameters.test.ts +520 -0
  84. package/src/hooks/use-copilot-action.ts +2 -1
  85. package/src/hooks/use-frontend-tool.ts +3 -6
  86. package/src/hooks/zod-parameters.ts +162 -0
  87. package/src/v2/components/A2UIMessageRenderer.ts +3 -3
  88. package/src/v2/components/LicenseWarningBanner.vue +31 -15
  89. package/src/v2/components/OpenGenerativeUIRenderer.ts +51 -373
  90. package/src/v2/components/__tests__/A2UICatalogContext.test.ts +1 -1
  91. package/src/v2/components/__tests__/LicenseWarningBanner.test.ts +20 -0
  92. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +55 -6
  93. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +1 -1
  94. package/src/v2/components/a2ui/A2UICatalogContext.ts +10 -102
  95. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +1 -1
  96. package/src/v2/components/a2ui.ts +1 -1
  97. package/src/v2/components/chat/CopilotChat.vue +19 -40
  98. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +14 -10
  99. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +14 -6
  100. package/src/v2/components/chat/CopilotChatInput.vue +40 -30
  101. package/src/v2/components/chat/CopilotChatMessageView.vue +72 -140
  102. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +31 -13
  103. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +60 -37
  104. package/src/v2/components/chat/CopilotChatToolCallItem.vue +3 -3
  105. package/src/v2/components/chat/CopilotChatToolCallsView.vue +15 -13
  106. package/src/v2/components/chat/CopilotChatView.vue +5 -5
  107. package/src/v2/components/chat/CopilotChatWorkSummary.vue +44 -18
  108. package/src/v2/components/chat/__tests__/CopilotChat.labels.test.ts +19 -4
  109. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.ts +26 -9
  110. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +33 -1
  111. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +247 -403
  112. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +1 -99
  113. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -19
  114. package/src/v2/components/chat/index.ts +0 -1
  115. package/src/v2/components/chat/tool-call-display-context.ts +2 -23
  116. package/src/v2/components/chat/tool-call-group-parts.ts +44 -85
  117. package/src/v2/components/chat/turn-work.ts +0 -9
  118. package/src/v2/components/chat/types.ts +4 -4
  119. package/src/v2/components/chat/use-chat-labels.ts +16 -0
  120. package/src/v2/components/icons/__tests__/icons.test.ts +72 -68
  121. package/src/v2/components/icons/index.ts +109 -27
  122. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +62 -1
  123. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +105 -0
  124. package/src/v2/hooks/__tests__/use-render-tool.test.ts +3 -6
  125. package/src/v2/hooks/__tests__/zod-regression.test.ts +5 -4
  126. package/src/v2/hooks/use-frontend-tool.ts +10 -10
  127. package/src/v2/hooks/use-render-activity-message.ts +27 -48
  128. package/src/v2/hooks/use-render-custom-messages.ts +14 -35
  129. package/src/v2/lib/processPartialHtml.ts +6 -21
  130. package/src/v2/lib/vue-core.ts +3 -5
  131. package/src/v2/providers/CopilotKitProvider.vue +53 -112
  132. package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +1 -1
  133. package/src/v2/providers/__tests__/CopilotKitProvider.headersAtSendTime.test.ts +3 -3
  134. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +3 -1
  135. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +17 -0
  136. package/src/v2/providers/types.ts +3 -49
  137. package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +28 -33
  138. package/src/v2/types/defineToolCallRenderer.ts +5 -4
  139. package/src/v2/types/human-in-the-loop.ts +8 -32
  140. package/src/v2/types/interrupt.ts +7 -31
  141. package/src/v2/types/vue-activity-message-renderer.ts +24 -15
  142. package/src/v2/types/vue-custom-message-renderer.ts +12 -16
  143. package/src/v2/types/vue-tool-call-renderer.ts +23 -38
  144. package/vite.config.ts +3 -4
  145. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts +0 -9
  146. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +0 -1
  147. package/dist/v2/components/chat/tool-call-layout.d.ts +0 -115
  148. package/dist/v2/components/chat/tool-call-layout.d.ts.map +0 -1
  149. package/dist/v2/lib/transcription-client.d.ts +0 -19
  150. package/dist/v2/lib/transcription-client.d.ts.map +0 -1
  151. package/src/v2/components/chat/BasicMarkdown.vue +0 -160
  152. package/src/v2/components/chat/__tests__/BasicMarkdown.test.ts +0 -69
  153. package/src/v2/components/chat/tool-call-layout.ts +0 -304
  154. package/src/v2/lib/__tests__/processPartialHtml.test.ts +0 -84
  155. package/src/v2/lib/__tests__/transcription-client.test.ts +0 -67
  156. package/src/v2/lib/transcription-client.ts +0 -151
@@ -1,4 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import type * as CoreModule from "@copilotkit/core";
2
3
  import { render } from "@testing-library/vue";
3
4
  import { ref } from "vue";
4
5
  import type { Ref } from "vue";
@@ -37,12 +38,25 @@ const mockCreate = vi.fn<MockCreateFn>(() => ({
37
38
  destroy: mockDestroy,
38
39
  }));
39
40
 
40
- vi.mock("@jetbrains/websandbox", () => ({
41
- default: {
42
- create: (localApi: Record<string, unknown>, options: SandboxOptions) =>
43
- mockCreate(localApi, options),
44
- },
45
- }));
41
+ // Run the real core controller against a fake websandbox
42
+ vi.mock("@copilotkit/core", async (importOriginal) => {
43
+ const actual = await importOriginal<typeof CoreModule>();
44
+ class TestOpenGenerativeUIController
45
+ extends actual.ɵOpenGenerativeUIController
46
+ {
47
+ constructor() {
48
+ super({
49
+ loadWebsandbox: async () => ({
50
+ create: (localApi, options) => mockCreate(localApi, options),
51
+ }),
52
+ });
53
+ }
54
+ }
55
+ return {
56
+ ...actual,
57
+ ɵOpenGenerativeUIController: TestOpenGenerativeUIController,
58
+ };
59
+ });
46
60
 
47
61
  async function flushImport() {
48
62
  await new Promise((resolve) => setTimeout(resolve, 0));
@@ -178,6 +192,41 @@ describe("OpenGenerativeUIRenderer", () => {
178
192
  expect(options.frameContent).toContain("v2");
179
193
  });
180
194
 
195
+ it("sizes the container to the height the final sandbox reports", async () => {
196
+ const iframe = document.createElement("iframe");
197
+ mockCreate.mockImplementationOnce((_localApi, options) => {
198
+ (options.frameContainer as HTMLElement).appendChild(iframe);
199
+ return {
200
+ iframe,
201
+ promise: Promise.resolve(),
202
+ run: mockRun,
203
+ destroy: mockDestroy,
204
+ };
205
+ });
206
+ const { getByTestId } = renderRenderer({
207
+ initialHeight: 150,
208
+ html: ["<body><p>Chart</p></body>"],
209
+ htmlComplete: true,
210
+ generating: false,
211
+ });
212
+ await flushImport();
213
+ expect(getByTestId("open-generative-ui-renderer").style.height).toBe(
214
+ "150px",
215
+ );
216
+
217
+ window.dispatchEvent(
218
+ new MessageEvent("message", {
219
+ source: iframe.contentWindow,
220
+ data: { type: "__ck_resize", height: 420 },
221
+ }),
222
+ );
223
+ await flushImport();
224
+
225
+ expect(getByTestId("open-generative-ui-renderer").style.height).toBe(
226
+ "420px",
227
+ );
228
+ });
229
+
181
230
  it("destroys sandbox on unmount", async () => {
182
231
  const mounted = renderRenderer({
183
232
  html: ["<head></head><body>bye</body>"],
@@ -1,6 +1,6 @@
1
1
  import { watch } from "vue";
2
2
  import type { ShallowRef } from "vue";
3
- import { z } from "zod";
3
+ import * as z from "zod";
4
4
  import type { CopilotKitCoreVue } from "../../lib/vue-core";
5
5
  import { defineToolCallRenderer } from "../../types/defineToolCallRenderer";
6
6
 
@@ -1,7 +1,11 @@
1
1
  import { computed, watch } from "vue";
2
2
  import type { ShallowRef } from "vue";
3
3
  import type { Catalog, ComponentApi } from "@a2ui/web_core/v0_9";
4
- import { zodToJsonSchema } from "zod-to-json-schema";
4
+ import {
5
+ A2UI_SCHEMA_CONTEXT_DESCRIPTION,
6
+ buildCatalogContextValue as buildContextValue,
7
+ extractCatalogComponentSchemas,
8
+ } from "@copilotkit/a2ui-renderer/catalog-schema";
5
9
  import {
6
10
  A2UI_DEFAULT_GENERATION_GUIDELINES,
7
11
  A2UI_DEFAULT_DESIGN_GUIDELINES,
@@ -9,22 +13,9 @@ import {
9
13
  import type { CopilotKitCoreVue } from "../../lib/vue-core";
10
14
  import { vueBasicCatalog } from "./catalog";
11
15
 
12
- /**
13
- * Context description used to identify the A2UI component schema in
14
- * RunAgentInput.context. Must match the constant in @ag-ui/a2ui-middleware
15
- * so the middleware can overwrite a frontend-provided schema with a
16
- * server-side one.
17
- *
18
- * Duplicated from @copilotkit/a2ui-renderer/catalog-utils to avoid pulling
19
- * in React dependencies.
20
- */
21
- const A2UI_SCHEMA_CONTEXT_DESCRIPTION =
22
- "A2UI Component Schema — available components for generating UI surfaces. " +
23
- "Use these component names and properties when creating A2UI operations.";
24
-
25
16
  /**
26
17
  * Build a context string describing the available A2UI catalog.
27
- * Vue-native equivalent of buildCatalogContextValue from a2ui-renderer.
18
+ * Defaults to the Vue basic catalog.
28
19
  *
29
20
  * When `options.includeSchema` is `false`, custom components are listed by name
30
21
  * only and their JSON schemas are omitted.
@@ -33,92 +24,7 @@ export function buildCatalogContextValue(
33
24
  catalog?: Catalog<ComponentApi>,
34
25
  options?: { includeSchema?: boolean },
35
26
  ): string {
36
- const resolved = catalog ?? vueBasicCatalog;
37
- const BASIC_CATALOG_ID =
38
- "https://a2ui.org/specification/v0_9/basic_catalog.json";
39
- const lines: string[] = [];
40
- lines.push("Available A2UI catalog:");
41
-
42
- if (resolved.id === BASIC_CATALOG_ID) {
43
- lines.push(`- ${resolved.id} (basic catalog)`);
44
- return lines.join("\n");
45
- }
46
-
47
- // Check if the resolved catalog extends the basic catalog
48
- let isSuperset = true;
49
- for (const name of vueBasicCatalog.components.keys()) {
50
- if (!resolved.components.has(name)) {
51
- isSuperset = false;
52
- break;
53
- }
54
- }
55
-
56
- // Identify custom components (not in basic catalog)
57
- const customNames: string[] = [];
58
- for (const name of resolved.components.keys()) {
59
- if (!vueBasicCatalog.components.has(name)) {
60
- customNames.push(name);
61
- }
62
- }
63
-
64
- lines.push(`- ${resolved.id}`);
65
- if (isSuperset) {
66
- lines.push(
67
- " Extends the basic catalog with all standard components plus:",
68
- );
69
- } else {
70
- lines.push(" Custom catalog (does NOT include all basic components).");
71
- lines.push(" Custom components:");
72
- }
73
-
74
- for (const name of customNames) {
75
- const comp = resolved.components.get(name);
76
- if (!comp) continue;
77
- if (options?.includeSchema === false) {
78
- lines.push(` - ${name}`);
79
- continue;
80
- }
81
- const jsonSchema = zodToJsonSchema(comp.schema);
82
- lines.push(` - ${name}:`);
83
- lines.push(
84
- ` ${JSON.stringify(jsonSchema, null, 2).split("\n").join("\n ")}`,
85
- );
86
- }
87
-
88
- return lines.join("\n");
89
- }
90
-
91
- /**
92
- * Extract component schemas from a catalog in A2UI v0.9 inline format.
93
- * Vue-native equivalent of extractCatalogComponentSchemas.
94
- */
95
- function extractCatalogComponentSchemas(catalog?: Catalog<ComponentApi>): {
96
- catalogId: string;
97
- components: Record<string, Record<string, unknown>>;
98
- } {
99
- const resolved = catalog ?? vueBasicCatalog;
100
- const components: Record<string, Record<string, unknown>> = {};
101
-
102
- for (const [name, comp] of resolved.components) {
103
- const zodSchema = zodToJsonSchema(comp.schema, {
104
- target: "jsonSchema2019-09",
105
- }) as { properties?: Record<string, unknown>; required?: string[] };
106
-
107
- components[name] = {
108
- allOf: [
109
- { $ref: "common_types.json#/$defs/ComponentCommon" },
110
- {
111
- properties: {
112
- component: { const: name },
113
- ...zodSchema.properties,
114
- },
115
- required: ["component", ...(zodSchema.required ?? [])],
116
- },
117
- ],
118
- };
119
- }
120
-
121
- return { catalogId: resolved.id, components };
27
+ return buildContextValue(catalog ?? vueBasicCatalog, options);
122
28
  }
123
29
 
124
30
  /**
@@ -165,7 +71,9 @@ export function registerA2UICatalogContext(
165
71
  // Register schema + generation/design guidelines
166
72
  const schemaValue = computed(() => {
167
73
  if (!options.includeSchema()) return null;
168
- return JSON.stringify(extractCatalogComponentSchemas(options.catalog()));
74
+ return JSON.stringify(
75
+ extractCatalogComponentSchemas(options.catalog() ?? vueBasicCatalog),
76
+ );
169
77
  });
170
78
 
171
79
  watch(
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, h, onBeforeUnmount, ref, watch } from "vue";
2
2
  import type { PropType, VNodeChild } from "vue";
3
- import { z } from "zod";
3
+ import * as z from "zod";
4
4
 
5
5
  /**
6
6
  * Shared pre-paint lifecycle states for the A2UI surface renderer, ported from
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
 
3
3
  export type A2UIOperation = Record<string, unknown>;
4
4
 
@@ -4,7 +4,6 @@ import {
4
4
  createAttachmentContent,
5
5
  DEFAULT_AGENT_ID,
6
6
  randomUUID,
7
- TranscriptionErrorCode,
8
7
  } from "@copilotkit/shared";
9
8
  import {
10
9
  computed,
@@ -17,7 +16,14 @@ import {
17
16
  useSlots,
18
17
  watch,
19
18
  } from "vue";
20
- import { CopilotKitCoreErrorCode, ɵisHttpAgent } from "@copilotkit/core";
19
+ import {
20
+ CopilotChatDefaultLabels,
21
+ CopilotKitCoreErrorCode,
22
+ TranscriptionError,
23
+ ɵisHttpAgent,
24
+ ɵtranscribeAudio,
25
+ ɵtranscriptionErrorMessageKey,
26
+ } from "@copilotkit/core";
21
27
  import type { Suggestion } from "@copilotkit/core";
22
28
  import CopilotChatConfigurationProvider from "../../providers/CopilotChatConfigurationProvider.vue";
23
29
  import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
@@ -28,10 +34,6 @@ import { useAgent } from "../../hooks/use-agent";
28
34
  import { useSuggestions } from "../../hooks/use-suggestions";
29
35
  import { useAttachments } from "../../hooks/use-attachments";
30
36
  import { useShallowStableRef } from "../../lib/shallow-stable";
31
- import {
32
- transcribeAudio,
33
- TranscriptionError,
34
- } from "../../lib/transcription-client";
35
37
  import CopilotChatView from "./CopilotChatView.vue";
36
38
  import { LastUserMessageKey } from "./last-user-message-context";
37
39
  import type { LastUserMessageState } from "./last-user-message-context";
@@ -159,6 +161,12 @@ const isConnecting = computed(
159
161
  );
160
162
  const stableLabels = useShallowStableRef(computed(() => props.labels));
161
163
  const resolvedLabels = computed(() => stableLabels.value);
164
+ // The labels this chat shows, the same merge its configuration provider makes.
165
+ const chatLabels = computed(() => ({
166
+ ...CopilotChatDefaultLabels,
167
+ ...existingConfig.value?.labels,
168
+ ...resolvedLabels.value,
169
+ }));
162
170
 
163
171
  // `useAgent` takes no threadId: it pins one only from a surrounding chat
164
172
  // configuration, and this call sits outside the provider rendered in this
@@ -771,48 +779,19 @@ async function handleFinishTranscribeWithAudio(audioBlob: Blob) {
771
779
  isTranscribing.value = true;
772
780
  try {
773
781
  transcriptionError.value = null;
774
- const result = await transcribeAudio(copilotkit.value, audioBlob);
782
+ const result = await ɵtranscribeAudio(copilotkit.value, audioBlob);
775
783
  const trimmedPrevious = inputValue.value.trim();
776
784
  inputValue.value = trimmedPrevious
777
785
  ? `${trimmedPrevious} ${result.text}`
778
786
  : result.text;
779
787
  } catch (error) {
780
788
  console.error("CopilotChat: Transcription failed", error);
789
+ const labels = chatLabels.value;
781
790
  if (error instanceof TranscriptionError) {
782
- const { code, retryable, message } = error.info;
783
- switch (code) {
784
- case TranscriptionErrorCode.RATE_LIMITED:
785
- transcriptionError.value = "Too many requests. Please wait a moment.";
786
- break;
787
- case TranscriptionErrorCode.AUTH_FAILED:
788
- transcriptionError.value =
789
- "Authentication error. Please check your configuration.";
790
- break;
791
- case TranscriptionErrorCode.AUDIO_TOO_LONG:
792
- transcriptionError.value =
793
- "Recording is too long. Please try a shorter recording.";
794
- break;
795
- case TranscriptionErrorCode.AUDIO_TOO_SHORT:
796
- transcriptionError.value =
797
- "Recording is too short. Please try again.";
798
- break;
799
- case TranscriptionErrorCode.INVALID_AUDIO_FORMAT:
800
- transcriptionError.value = "Audio format not supported.";
801
- break;
802
- case TranscriptionErrorCode.SERVICE_NOT_CONFIGURED:
803
- transcriptionError.value = "Transcription service is not available.";
804
- break;
805
- case TranscriptionErrorCode.NETWORK_ERROR:
806
- transcriptionError.value =
807
- "Network error. Please check your connection.";
808
- break;
809
- default:
810
- transcriptionError.value = retryable
811
- ? "Transcription failed. Please try again."
812
- : message;
813
- }
791
+ const key = ɵtranscriptionErrorMessageKey(error.info);
792
+ transcriptionError.value = key ? labels[key] : error.info.message;
814
793
  } else {
815
- transcriptionError.value = "Transcription failed. Please try again.";
794
+ transcriptionError.value = labels.transcriptionErrorGenericText;
816
795
  }
817
796
  } finally {
818
797
  isTranscribing.value = false;
@@ -9,8 +9,7 @@ import type { Attachment } from "@copilotkit/shared";
9
9
  import type { CopilotChatAttachmentQueueProps } from "./types";
10
10
  import { IconPlay } from "../icons";
11
11
  import CopilotChatLightbox from "./CopilotChatLightbox.vue";
12
- import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
13
- import { CopilotChatDefaultLabels } from "../../providers/types";
12
+ import { useChatLabels } from "./use-chat-labels";
14
13
 
15
14
  const props = withDefaults(defineProps<CopilotChatAttachmentQueueProps>(), {
16
15
  attachments: () => [],
@@ -21,8 +20,7 @@ const emit = defineEmits<{
21
20
  "remove-attachment": [id: string];
22
21
  }>();
23
22
 
24
- const config = useCopilotChatConfiguration();
25
- const labels = computed(() => config.value?.labels ?? CopilotChatDefaultLabels);
23
+ const labels = useChatLabels();
26
24
 
27
25
  const activeLightboxAttachment = ref<Attachment | null>(null);
28
26
  const documentBlobUrl = ref<string | null>(null);
@@ -32,10 +30,10 @@ const lightboxLabel = computed(() => {
32
30
  if (!attachment) return undefined;
33
31
  if (attachment.filename) return attachment.filename;
34
32
  return attachment.type === "image"
35
- ? "Image attachment"
33
+ ? labels.value.attachmentImageAltText
36
34
  : attachment.type === "video"
37
35
  ? "Video attachment"
38
- : "Document";
36
+ : labels.value.attachmentDocumentLabel;
39
37
  });
40
38
  const isPreviewableDocument = computed(() => {
41
39
  const attachment = activeLightboxAttachment.value;
@@ -267,7 +265,7 @@ function createBlobUrl(
267
265
  class="cpk-attachment-queue-document-name"
268
266
  data-testid="copilot-chat-attachment-document-filename"
269
267
  >
270
- {{ attachment.filename || "Document" }}
268
+ {{ attachment.filename || labels.attachmentDocumentLabel }}
271
269
  </span>
272
270
  <span
273
271
  v-if="attachment.size != null"
@@ -369,10 +367,16 @@ function createBlobUrl(
369
367
  </div>
370
368
  <div class="cpk-attachment-queue-document-card-details">
371
369
  <div class="cpk-attachment-queue-document-card-name">
372
- {{ activeLightboxAttachment.filename || "Document" }}
370
+ {{
371
+ activeLightboxAttachment.filename ||
372
+ labels.attachmentDocumentLabel
373
+ }}
373
374
  </div>
374
375
  <div class="cpk-attachment-queue-document-card-meta">
375
- {{ activeLightboxAttachment.source.mimeType || "Unknown type" }}
376
+ {{
377
+ activeLightboxAttachment.source.mimeType ||
378
+ labels.attachmentUnknownTypeText
379
+ }}
376
380
  {{
377
381
  activeLightboxAttachment.size != null
378
382
  ? ` · ${formatFileSize(activeLightboxAttachment.size)}`
@@ -381,7 +385,7 @@ function createBlobUrl(
381
385
  </div>
382
386
  </div>
383
387
  <div class="cpk-attachment-queue-document-card-note">
384
- No preview available for this file type
388
+ {{ labels.attachmentNoPreviewText }}
385
389
  </div>
386
390
  </div>
387
391
  </template>
@@ -4,19 +4,27 @@ import { getDocumentIcon, getSourceUrl } from "@copilotkit/shared";
4
4
  import type { CopilotChatAttachmentRendererProps } from "./types";
5
5
  import CopilotChatLightbox from "./CopilotChatLightbox.vue";
6
6
  import { useLightbox } from "./use-lightbox";
7
+ import { useChatLabels } from "./use-chat-labels";
7
8
 
8
9
  const props = withDefaults(defineProps<CopilotChatAttachmentRendererProps>(), {
9
10
  filename: undefined,
10
11
  className: "",
11
12
  });
12
13
 
14
+ const labels = useChatLabels();
13
15
  const imageLoadFailed = ref(false);
14
16
  const sourceUrl = computed(() => getSourceUrl(props.source));
15
17
  const documentLabel = computed(
16
- () => props.filename || props.source.mimeType || "Unknown type",
18
+ () =>
19
+ props.filename ||
20
+ props.source.mimeType ||
21
+ labels.value.attachmentUnknownTypeText,
17
22
  );
18
23
  const documentAriaLabel = computed(
19
- () => props.filename || props.source.mimeType || "Document attachment",
24
+ () =>
25
+ props.filename ||
26
+ props.source.mimeType ||
27
+ labels.value.attachmentDocumentAltText,
20
28
  );
21
29
 
22
30
  const imageButton = ref<HTMLButtonElement | null>(null);
@@ -43,7 +51,7 @@ const {
43
51
  <img
44
52
  ref="thumbnailRef"
45
53
  :src="sourceUrl"
46
- alt="Image attachment"
54
+ :alt="labels.attachmentImageAltText"
47
55
  class="cpk-attachment-renderer cpk-attachment-renderer-image"
48
56
  :class="props.className"
49
57
  data-testid="copilot-chat-attachment-renderer-image"
@@ -52,14 +60,14 @@ const {
52
60
  <!-- Teleported to the body; inside the button only to keep one root. -->
53
61
  <CopilotChatLightbox
54
62
  v-if="lightboxOpen"
55
- label="Image attachment"
63
+ :label="labels.attachmentImageAltText"
56
64
  :return-focus-to="imageButton"
57
65
  @close="closeLightbox"
58
66
  >
59
67
  <img
60
68
  :style="{ viewTransitionName: vtName }"
61
69
  :src="sourceUrl"
62
- alt="Image attachment"
70
+ :alt="labels.attachmentImageAltText"
63
71
  class="cpk-attachment-renderer-lightbox-image"
64
72
  data-testid="copilot-chat-attachment-renderer-lightbox-image"
65
73
  />
@@ -71,7 +79,7 @@ const {
71
79
  :class="props.className"
72
80
  data-testid="copilot-chat-attachment-renderer-image-fallback"
73
81
  >
74
- <span>Failed to load image</span>
82
+ <span>{{ labels.attachmentImageLoadErrorText }}</span>
75
83
  </div>
76
84
 
77
85
  <div
@@ -8,8 +8,7 @@ import {
8
8
  useAttrs,
9
9
  watch,
10
10
  } from "vue";
11
- import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
12
- import { CopilotChatDefaultLabels } from "../../providers/types";
11
+ import { ɵcomposerButtonState, ɵresolveComposerKey } from "@copilotkit/core";
13
12
  import {
14
13
  IconArrowUp,
15
14
  IconCheck,
@@ -29,6 +28,7 @@ import type {
29
28
  import { useCopilotId } from "../ui/use-id";
30
29
  import type { CopilotChatAudioRecorderRef } from "./audioRecorder";
31
30
  import type { CopilotChatInputMode, ToolsMenuItem } from "./types";
31
+ import { useChatLabels } from "./use-chat-labels";
32
32
 
33
33
  defineOptions({ inheritAttrs: false });
34
34
 
@@ -107,7 +107,6 @@ const emit = defineEmits<{
107
107
  }>();
108
108
 
109
109
  const attrs = useAttrs();
110
- const config = useCopilotChatConfiguration();
111
110
  const shellRef = ref<HTMLElement | null>(null);
112
111
  const textareaRef = ref<HTMLTextAreaElement | null>(null);
113
112
  const gridRef = ref<HTMLElement | null>(null);
@@ -148,16 +147,13 @@ const isControlled = computed(() => props.modelValue !== undefined);
148
147
  const inputValue = computed(() =>
149
148
  isControlled.value ? (props.modelValue ?? "") : localValue.value,
150
149
  );
151
- const labels = computed(() => config.value?.labels ?? CopilotChatDefaultLabels);
150
+ const labels = useChatLabels();
152
151
  const resolvedPlaceholder = computed(
153
152
  () => props.placeholder ?? labels.value.chatInputPlaceholder,
154
153
  );
155
154
  const isExpanded = computed(
156
155
  () => props.mode === "input" && layout.value === "expanded",
157
156
  );
158
- const isProcessing = computed(
159
- () => props.mode !== "transcribe" && props.isRunning,
160
- );
161
157
  const shouldShowDisclaimer = computed(
162
158
  () => props.showDisclaimer ?? props.positioning === "absolute",
163
159
  );
@@ -177,16 +173,20 @@ const hasFinishTranscribeAction = computed(
177
173
  typeof props.onFinishTranscribe === "function" ||
178
174
  typeof props.onFinishTranscribeWithAudio === "function",
179
175
  );
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,
176
+ const composerInput = computed(() => ({
177
+ mode: props.mode,
178
+ isRunning: props.isRunning,
179
+ value: inputValue.value,
180
+ canSubmit: hasSubmitAction.value,
181
+ canStop: hasStopAction.value,
182
+ disabled: props.disabled,
183
+ }));
184
+ const composerButton = computed(() =>
185
+ ɵcomposerButtonState(composerInput.value),
189
186
  );
187
+ const isProcessing = computed(() => composerButton.value.isProcessing);
188
+ const sendDisabled = computed(() => composerButton.value.disabled);
189
+ const showsStop = computed(() => composerButton.value.action === "stop");
190
190
 
191
191
  const containerClass = computed(() => [
192
192
  "cpk-chat-input",
@@ -408,14 +408,16 @@ function handleInput(event: Event) {
408
408
  updateSlashState(nextValue);
409
409
  }
410
410
 
411
+ // The button stops a run whatever the input holds. Enter differs: see
412
+ // handleKeydown.
411
413
  function handleSendButtonClick() {
412
- if (isProcessing.value) {
413
- if (hasStopAction.value) {
414
- emit("stop");
415
- }
414
+ if (showsStop.value) {
415
+ emit("stop");
416
416
  return;
417
417
  }
418
- submit();
418
+ if (!isProcessing.value) {
419
+ submit();
420
+ }
419
421
  }
420
422
 
421
423
  function handleKeydown(event: KeyboardEvent) {
@@ -473,15 +475,23 @@ function handleKeydown(event: KeyboardEvent) {
473
475
  }
474
476
  }
475
477
 
476
- if (event.key === "Enter" && !event.shiftKey && props.mode === "input") {
478
+ if (props.mode !== "input") {
479
+ return;
480
+ }
481
+
482
+ // Enter with text sends, also while a run is going. Enter in an empty input
483
+ // stops the run. Read the textarea: a controlled modelValue can lag a tick.
484
+ const action = ɵresolveComposerKey(event, {
485
+ ...composerInput.value,
486
+ value: textareaRef.value?.value ?? inputValue.value,
487
+ });
488
+ if (event.key === "Enter" && !event.shiftKey) {
477
489
  event.preventDefault();
478
- if (isProcessing.value) {
479
- if (hasStopAction.value) {
480
- emit("stop");
481
- }
482
- return;
483
- }
490
+ }
491
+ if (action === "send") {
484
492
  submit();
493
+ } else if (action === "stop") {
494
+ emit("stop");
485
495
  }
486
496
  }
487
497
 
@@ -1295,7 +1305,7 @@ onBeforeUnmount(() => {
1295
1305
  type="button"
1296
1306
  data-testid="copilot-chat-input-send"
1297
1307
  :aria-label="
1298
- isProcessing && hasStopAction
1308
+ showsStop
1299
1309
  ? labels.chatInputToolbarStopButtonLabel
1300
1310
  : labels.chatInputToolbarSendButtonLabel
1301
1311
  "
@@ -1304,7 +1314,7 @@ onBeforeUnmount(() => {
1304
1314
  @click="handleSendButtonClick"
1305
1315
  >
1306
1316
  <IconSquare
1307
- v-if="isProcessing && hasStopAction"
1317
+ v-if="showsStop"
1308
1318
  class="cpk-chat-input-stop-icon"
1309
1319
  />
1310
1320
  <IconArrowUp v-else class="cpk-chat-input-send-icon" />