@copilotkit/react-core 1.55.0-next.8 → 1.55.0
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.
- package/CHANGELOG.md +48 -5
- package/dist/{copilotkit-DNYSFuz5.mjs → copilotkit-BY5S1-0P.mjs} +2772 -858
- package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
- package/dist/{copilotkit-Dy5w3qEV.d.mts → copilotkit-BuhSUZHb.d.mts} +230 -17
- package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
- package/dist/{copilotkit-B3Mb1yVE.cjs → copilotkit-Bz5-ImDl.cjs} +2776 -832
- package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
- package/dist/{copilotkit-DBzgOMby.d.cts → copilotkit-dwDWYpya.d.cts} +230 -17
- package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +9 -4
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1624 -396
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +13 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +3 -3
- package/dist/v2/index.d.mts +3 -3
- package/dist/v2/index.mjs +3 -2
- package/dist/v2/index.umd.js +2746 -790
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +62 -54
- package/scripts/scope-preflight.mjs +1 -2
- package/src/components/CopilotListeners.tsx +41 -8
- package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +92 -0
- package/src/components/copilot-provider/copilotkit-props.tsx +4 -2
- package/src/components/copilot-provider/copilotkit.tsx +3 -3
- package/src/components/toast/toast-provider.tsx +269 -194
- package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +27 -16
- package/src/hooks/use-copilot-chat_internal.ts +15 -4
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
- package/src/v2/__tests__/utils/test-helpers.tsx +107 -7
- package/src/v2/a2ui/A2UICatalogContext.tsx +79 -0
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +125 -37
- package/src/v2/a2ui/A2UIToolCallRenderer.tsx +290 -0
- package/src/v2/components/CopilotKitInspector.tsx +2 -0
- package/src/v2/components/OpenGenerativeUIRenderer.tsx +598 -0
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +665 -0
- package/src/v2/components/chat/CopilotChat.tsx +197 -52
- package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +17 -2
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +481 -0
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +139 -0
- package/src/v2/components/chat/CopilotChatInput.tsx +146 -77
- package/src/v2/components/chat/CopilotChatMessageView.tsx +260 -151
- package/src/v2/components/chat/CopilotChatSuggestionView.tsx +1 -0
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +54 -0
- package/src/v2/components/chat/CopilotChatView.tsx +179 -66
- package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +168 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +63 -2
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +544 -1
- package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +268 -0
- package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +249 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +60 -3
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +138 -0
- package/src/v2/components/chat/index.ts +9 -0
- package/src/v2/components/chat/scroll-element-context.ts +13 -0
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +8 -0
- package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +327 -0
- package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +1003 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +13 -2
- package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +23 -4
- package/src/v2/hooks/__tests__/use-threads.test.tsx +54 -0
- package/src/v2/hooks/index.ts +5 -0
- package/src/v2/hooks/use-agent.tsx +220 -15
- package/src/v2/hooks/use-attachments.tsx +269 -0
- package/src/v2/hooks/use-frontend-tool.tsx +5 -2
- package/src/v2/hooks/use-render-activity-message.tsx +9 -2
- package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
- package/src/v2/hooks/use-threads.tsx +35 -15
- package/src/v2/index.ts +5 -1
- package/src/v2/lib/__tests__/processPartialHtml.test.ts +112 -0
- package/src/v2/lib/__tests__/slots.test.ts +56 -0
- package/src/v2/lib/processPartialHtml.ts +45 -0
- package/src/v2/lib/slots.tsx +42 -1
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +9 -3
- package/src/v2/providers/CopilotKitProvider.tsx +268 -32
- package/src/v2/providers/SandboxFunctionsContext.ts +10 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +198 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +71 -0
- package/src/v2/providers/index.ts +7 -0
- package/src/v2/styles/globals.css +2 -1
- package/src/v2/types/index.ts +1 -0
- package/src/v2/types/sandbox-function.ts +11 -0
- package/dist/copilotkit-B3Mb1yVE.cjs.map +0 -1
- package/dist/copilotkit-DBzgOMby.d.cts.map +0 -1
- package/dist/copilotkit-DNYSFuz5.mjs.map +0 -1
- package/dist/copilotkit-Dy5w3qEV.d.mts.map +0 -1
- package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useAgent } from "../../hooks/use-agent";
|
|
2
|
+
import { useAttachments } from "../../hooks/use-attachments";
|
|
2
3
|
import { useSuggestions } from "../../hooks/use-suggestions";
|
|
3
4
|
import { CopilotChatView, CopilotChatViewProps } from "./CopilotChatView";
|
|
4
5
|
import { CopilotChatInputMode } from "./CopilotChatInput";
|
|
@@ -12,16 +13,22 @@ import {
|
|
|
12
13
|
randomUUID,
|
|
13
14
|
TranscriptionErrorCode,
|
|
14
15
|
} from "@copilotkit/shared";
|
|
16
|
+
import type { AttachmentsConfig, InputContent } from "@copilotkit/shared";
|
|
15
17
|
import { Suggestion, CopilotKitCoreErrorCode } from "@copilotkit/core";
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
+
import React, {
|
|
19
|
+
useCallback,
|
|
20
|
+
useEffect,
|
|
21
|
+
useMemo,
|
|
22
|
+
useRef,
|
|
23
|
+
useState,
|
|
24
|
+
} from "react";
|
|
18
25
|
import {
|
|
19
26
|
useCopilotKit,
|
|
20
27
|
useLicenseContext,
|
|
21
28
|
} from "../../providers/CopilotKitProvider";
|
|
22
29
|
import { InlineFeatureWarning } from "../../components/license-warning-banner";
|
|
23
30
|
import { AbstractAgent, HttpAgent } from "@ag-ui/client";
|
|
24
|
-
import { renderSlot, SlotValue } from "../../lib/slots";
|
|
31
|
+
import { renderSlot, useShallowStableRef, SlotValue } from "../../lib/slots";
|
|
25
32
|
import {
|
|
26
33
|
transcribeAudio,
|
|
27
34
|
TranscriptionError,
|
|
@@ -34,12 +41,22 @@ export type CopilotChatProps = Omit<
|
|
|
34
41
|
| "suggestions"
|
|
35
42
|
| "suggestionLoadingIndexes"
|
|
36
43
|
| "onSelectSuggestion"
|
|
44
|
+
// Attachment state props — managed internally based on `attachments` config
|
|
45
|
+
| "attachments"
|
|
46
|
+
| "onRemoveAttachment"
|
|
47
|
+
| "onAddFile"
|
|
48
|
+
| "dragOver"
|
|
49
|
+
| "onDragOver"
|
|
50
|
+
| "onDragLeave"
|
|
51
|
+
| "onDrop"
|
|
37
52
|
> & {
|
|
38
53
|
agentId?: string;
|
|
39
54
|
threadId?: string;
|
|
40
55
|
labels?: Partial<CopilotChatLabels>;
|
|
41
56
|
chatView?: SlotValue<typeof CopilotChatView>;
|
|
42
57
|
isModalDefaultOpen?: boolean;
|
|
58
|
+
/** Enable multimodal file attachments (images, audio, video, documents). */
|
|
59
|
+
attachments?: AttachmentsConfig;
|
|
43
60
|
/**
|
|
44
61
|
* Error handler scoped to this chat's agent. Fires in addition to the
|
|
45
62
|
* provider-level onError (does not suppress it). Receives only errors
|
|
@@ -50,6 +67,18 @@ export type CopilotChatProps = Omit<
|
|
|
50
67
|
code: CopilotKitCoreErrorCode;
|
|
51
68
|
context: Record<string, any>;
|
|
52
69
|
}) => void | Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Throttle interval (in milliseconds) for re-renders triggered by message
|
|
72
|
+
* change notifications. Overrides the provider-level `defaultThrottleMs`
|
|
73
|
+
* for this chat instance. Forwarded to the internal `useAgent()` hook,
|
|
74
|
+
* which resolves the effective throttle value.
|
|
75
|
+
*
|
|
76
|
+
* @default undefined — inherits from provider `defaultThrottleMs`;
|
|
77
|
+
* if that is also unset, re-renders are unthrottled. Note: passing
|
|
78
|
+
* `throttleMs={0}` explicitly disables throttling for this instance
|
|
79
|
+
* even when the provider specifies a non-zero `defaultThrottleMs`.
|
|
80
|
+
*/
|
|
81
|
+
throttleMs?: number;
|
|
53
82
|
};
|
|
54
83
|
export function CopilotChat({
|
|
55
84
|
agentId,
|
|
@@ -57,7 +86,9 @@ export function CopilotChat({
|
|
|
57
86
|
labels,
|
|
58
87
|
chatView,
|
|
59
88
|
isModalDefaultOpen,
|
|
89
|
+
attachments: attachmentsConfig,
|
|
60
90
|
onError,
|
|
91
|
+
throttleMs,
|
|
61
92
|
...props
|
|
62
93
|
}: CopilotChatProps) {
|
|
63
94
|
// Check for existing configuration provider
|
|
@@ -71,7 +102,11 @@ export function CopilotChat({
|
|
|
71
102
|
[threadId, existingConfig?.threadId],
|
|
72
103
|
);
|
|
73
104
|
|
|
74
|
-
const { agent } = useAgent({
|
|
105
|
+
const { agent } = useAgent({
|
|
106
|
+
agentId: resolvedAgentId,
|
|
107
|
+
threadId: resolvedThreadId,
|
|
108
|
+
throttleMs,
|
|
109
|
+
});
|
|
75
110
|
const { copilotkit } = useCopilotKit();
|
|
76
111
|
const { suggestions: autoSuggestions } = useSuggestions({
|
|
77
112
|
agentId: resolvedAgentId,
|
|
@@ -127,6 +162,21 @@ export function CopilotChat({
|
|
|
127
162
|
);
|
|
128
163
|
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
129
164
|
|
|
165
|
+
// Attachments
|
|
166
|
+
const {
|
|
167
|
+
attachments: selectedAttachments,
|
|
168
|
+
enabled: attachmentsEnabled,
|
|
169
|
+
dragOver,
|
|
170
|
+
fileInputRef,
|
|
171
|
+
containerRef: chatContainerRef,
|
|
172
|
+
handleFileUpload,
|
|
173
|
+
handleDragOver,
|
|
174
|
+
handleDragLeave,
|
|
175
|
+
handleDrop,
|
|
176
|
+
removeAttachment,
|
|
177
|
+
consumeAttachments,
|
|
178
|
+
} = useAttachments({ config: attachmentsConfig });
|
|
179
|
+
|
|
130
180
|
// Check if transcription is enabled
|
|
131
181
|
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
132
182
|
|
|
@@ -164,7 +214,6 @@ export function CopilotChat({
|
|
|
164
214
|
console.error("CopilotChat: connectAgent failed", error);
|
|
165
215
|
}
|
|
166
216
|
};
|
|
167
|
-
agent.threadId = resolvedThreadId;
|
|
168
217
|
connect(agent);
|
|
169
218
|
return () => {
|
|
170
219
|
// Abort the HTTP request and detach the active run.
|
|
@@ -184,11 +233,47 @@ export function CopilotChat({
|
|
|
184
233
|
|
|
185
234
|
const onSubmitInput = useCallback(
|
|
186
235
|
async (value: string) => {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
236
|
+
// Block if uploads in progress
|
|
237
|
+
const hasUploading = selectedAttachments.some(
|
|
238
|
+
(a) => a.status === "uploading",
|
|
239
|
+
);
|
|
240
|
+
if (hasUploading) {
|
|
241
|
+
console.error(
|
|
242
|
+
"[CopilotKit] Cannot send while attachments are uploading",
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const readyAttachments = consumeAttachments();
|
|
248
|
+
|
|
249
|
+
if (readyAttachments.length > 0) {
|
|
250
|
+
const contentParts: InputContent[] = [];
|
|
251
|
+
if (value.trim()) {
|
|
252
|
+
contentParts.push({ type: "text", text: value });
|
|
253
|
+
}
|
|
254
|
+
for (const att of readyAttachments) {
|
|
255
|
+
contentParts.push({
|
|
256
|
+
type: att.type,
|
|
257
|
+
source: att.source,
|
|
258
|
+
metadata: {
|
|
259
|
+
...(att.filename ? { filename: att.filename } : {}),
|
|
260
|
+
...att.metadata,
|
|
261
|
+
},
|
|
262
|
+
} as InputContent);
|
|
263
|
+
}
|
|
264
|
+
agent.addMessage({
|
|
265
|
+
id: randomUUID(),
|
|
266
|
+
role: "user",
|
|
267
|
+
content: contentParts,
|
|
268
|
+
});
|
|
269
|
+
} else {
|
|
270
|
+
agent.addMessage({
|
|
271
|
+
id: randomUUID(),
|
|
272
|
+
role: "user",
|
|
273
|
+
content: value,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
192
277
|
// Clear input after submitting
|
|
193
278
|
setInputValue("");
|
|
194
279
|
try {
|
|
@@ -199,7 +284,7 @@ export function CopilotChat({
|
|
|
199
284
|
},
|
|
200
285
|
// copilotkit is intentionally excluded — it is a stable ref that never changes.
|
|
201
286
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
202
|
-
[agent],
|
|
287
|
+
[agent, selectedAttachments, consumeAttachments],
|
|
203
288
|
);
|
|
204
289
|
|
|
205
290
|
const handleSelectSuggestion = useCallback(
|
|
@@ -334,22 +419,38 @@ export function CopilotChat({
|
|
|
334
419
|
}
|
|
335
420
|
}, [transcriptionError]);
|
|
336
421
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
{
|
|
345
|
-
...restProps,
|
|
346
|
-
...(typeof providedMessageView === "string"
|
|
347
|
-
? { messageView: { className: providedMessageView } }
|
|
348
|
-
: providedMessageView !== undefined
|
|
349
|
-
? { messageView: providedMessageView }
|
|
350
|
-
: {}),
|
|
351
|
-
},
|
|
422
|
+
// Stabilize slot object references so inline props (new object reference on
|
|
423
|
+
// every parent render) don't defeat MemoizedSlotWrapper's shallow equality
|
|
424
|
+
// check and cause unnecessary re-renders of the message list on each keystroke.
|
|
425
|
+
const stableMessageView = useShallowStableRef(
|
|
426
|
+
typeof providedMessageView === "string"
|
|
427
|
+
? { className: providedMessageView }
|
|
428
|
+
: providedMessageView,
|
|
352
429
|
);
|
|
430
|
+
const stableSuggestionView = useShallowStableRef(providedSuggestionView);
|
|
431
|
+
|
|
432
|
+
// Stabilize the `onAddFile` handler. Without useCallback, a new arrow
|
|
433
|
+
// function is created inline on every render, causing CopilotChatView to
|
|
434
|
+
// re-render on every keystroke even when nothing else changed.
|
|
435
|
+
const handleAddFile = useCallback(() => {
|
|
436
|
+
// Delay to let Radix dropdown menu close before triggering file input
|
|
437
|
+
setTimeout(() => {
|
|
438
|
+
fileInputRef.current?.click();
|
|
439
|
+
}, 100);
|
|
440
|
+
}, []);
|
|
441
|
+
|
|
442
|
+
// Use shallow spread instead of ts-deepmerge. ts-deepmerge deep-clones plain
|
|
443
|
+
// objects even from a single source, which would defeat the reference
|
|
444
|
+
// stability we just established for stableMessageView and other slot values.
|
|
445
|
+
const mergedProps: Partial<CopilotChatViewProps> = {
|
|
446
|
+
isRunning: agent.isRunning,
|
|
447
|
+
suggestions: autoSuggestions,
|
|
448
|
+
onSelectSuggestion: handleSelectSuggestion,
|
|
449
|
+
suggestionView: stableSuggestionView,
|
|
450
|
+
...restProps,
|
|
451
|
+
};
|
|
452
|
+
if (stableMessageView !== undefined)
|
|
453
|
+
mergedProps.messageView = stableMessageView;
|
|
353
454
|
|
|
354
455
|
const hasMessages = agent.messages.length > 0;
|
|
355
456
|
const shouldAllowStop = agent.isRunning && hasMessages;
|
|
@@ -365,15 +466,39 @@ export function CopilotChat({
|
|
|
365
466
|
? "processing"
|
|
366
467
|
: transcribeMode;
|
|
367
468
|
|
|
368
|
-
// Memoize messages array
|
|
369
|
-
//
|
|
370
|
-
|
|
469
|
+
// Memoize messages array — only create a new reference when content changes.
|
|
470
|
+
// We build a lightweight fingerprint instead of JSON.stringify to avoid
|
|
471
|
+
// serializing large base64 attachment data on every render. The key captures:
|
|
472
|
+
// - message id, role, content length (text streaming)
|
|
473
|
+
// - content part count (multimodal additions)
|
|
474
|
+
// - tool call ids + argument lengths (tool call streaming)
|
|
475
|
+
const messagesMemoKey = agent.messages
|
|
476
|
+
.map((m) => {
|
|
477
|
+
const contentKey =
|
|
478
|
+
typeof m.content === "string"
|
|
479
|
+
? m.content.length
|
|
480
|
+
: Array.isArray(m.content)
|
|
481
|
+
? m.content.length
|
|
482
|
+
: 0;
|
|
483
|
+
const toolCallsKey =
|
|
484
|
+
"toolCalls" in m && Array.isArray(m.toolCalls)
|
|
485
|
+
? m.toolCalls
|
|
486
|
+
.map(
|
|
487
|
+
(tc: any) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`,
|
|
488
|
+
)
|
|
489
|
+
.join(";")
|
|
490
|
+
: "";
|
|
491
|
+
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
|
|
492
|
+
})
|
|
493
|
+
.join(",");
|
|
371
494
|
const messages = useMemo(
|
|
372
495
|
() => [...agent.messages],
|
|
373
|
-
|
|
496
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
497
|
+
[messagesMemoKey],
|
|
374
498
|
);
|
|
375
499
|
|
|
376
|
-
const finalProps =
|
|
500
|
+
const finalProps: CopilotChatViewProps = {
|
|
501
|
+
...mergedProps,
|
|
377
502
|
messages,
|
|
378
503
|
// Input behavior props
|
|
379
504
|
onSubmitMessage: onSubmitInput,
|
|
@@ -388,7 +513,15 @@ export function CopilotChat({
|
|
|
388
513
|
onFinishTranscribeWithAudio: showTranscription
|
|
389
514
|
? handleFinishTranscribeWithAudio
|
|
390
515
|
: undefined,
|
|
391
|
-
|
|
516
|
+
// Attachment props
|
|
517
|
+
attachments: selectedAttachments,
|
|
518
|
+
onRemoveAttachment: removeAttachment,
|
|
519
|
+
onAddFile: attachmentsEnabled ? handleAddFile : undefined,
|
|
520
|
+
dragOver,
|
|
521
|
+
onDragOver: handleDragOver,
|
|
522
|
+
onDragLeave: handleDragLeave,
|
|
523
|
+
onDrop: handleDrop,
|
|
524
|
+
};
|
|
392
525
|
|
|
393
526
|
// Always create a provider with merged values
|
|
394
527
|
// This ensures priority: props > existing config > defaults
|
|
@@ -401,26 +534,38 @@ export function CopilotChat({
|
|
|
401
534
|
labels={labels}
|
|
402
535
|
isModalDefaultOpen={isModalDefaultOpen}
|
|
403
536
|
>
|
|
404
|
-
{
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
537
|
+
<div ref={chatContainerRef} style={{ display: "contents" }}>
|
|
538
|
+
{attachmentsEnabled && (
|
|
539
|
+
<input
|
|
540
|
+
type="file"
|
|
541
|
+
multiple
|
|
542
|
+
ref={fileInputRef}
|
|
543
|
+
onChange={handleFileUpload}
|
|
544
|
+
accept={attachmentsConfig?.accept ?? "*/*"}
|
|
545
|
+
style={{ display: "none" }}
|
|
546
|
+
/>
|
|
547
|
+
)}
|
|
548
|
+
{!isChatLicensed && <InlineFeatureWarning featureName="Chat" />}
|
|
549
|
+
{transcriptionError && (
|
|
550
|
+
<div
|
|
551
|
+
style={{
|
|
552
|
+
position: "absolute",
|
|
553
|
+
bottom: "100px",
|
|
554
|
+
left: "50%",
|
|
555
|
+
transform: "translateX(-50%)",
|
|
556
|
+
backgroundColor: "#ef4444",
|
|
557
|
+
color: "white",
|
|
558
|
+
padding: "8px 16px",
|
|
559
|
+
borderRadius: "8px",
|
|
560
|
+
fontSize: "14px",
|
|
561
|
+
zIndex: 50,
|
|
562
|
+
}}
|
|
563
|
+
>
|
|
564
|
+
{transcriptionError}
|
|
565
|
+
</div>
|
|
566
|
+
)}
|
|
567
|
+
{RenderedChatView}
|
|
568
|
+
</div>
|
|
424
569
|
</CopilotChatConfigurationProvider>
|
|
425
570
|
);
|
|
426
571
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AssistantMessage, Message } from "@ag-ui/core";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
Copy,
|
|
5
5
|
Check,
|
|
@@ -265,10 +265,25 @@ export namespace CopilotChatAssistantMessage {
|
|
|
265
265
|
const config = useCopilotChatConfiguration();
|
|
266
266
|
const labels = config?.labels ?? CopilotChatDefaultLabels;
|
|
267
267
|
const [copied, setCopied] = useState(false);
|
|
268
|
+
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
269
|
+
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
return () => {
|
|
272
|
+
if (timerRef.current !== null) {
|
|
273
|
+
clearTimeout(timerRef.current);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}, []);
|
|
268
277
|
|
|
269
278
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
270
279
|
setCopied(true);
|
|
271
|
-
|
|
280
|
+
if (timerRef.current !== null) {
|
|
281
|
+
clearTimeout(timerRef.current);
|
|
282
|
+
}
|
|
283
|
+
timerRef.current = setTimeout(() => {
|
|
284
|
+
timerRef.current = null;
|
|
285
|
+
setCopied(false);
|
|
286
|
+
}, 2000);
|
|
272
287
|
|
|
273
288
|
if (onClick) {
|
|
274
289
|
onClick(event);
|