@copilotkit/react-core 1.55.0-next.9 → 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 +36 -6
- package/dist/{copilotkit-DeOzjPsb.mjs → copilotkit-BY5S1-0P.mjs} +2402 -552
- package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
- package/dist/{copilotkit-BqcyhQjT.d.mts → copilotkit-BuhSUZHb.d.mts} +228 -17
- package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
- package/dist/{copilotkit-BDNjFNmk.cjs → copilotkit-Bz5-ImDl.cjs} +2421 -541
- package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
- package/dist/{copilotkit-l-IBF4Xp.d.cts → copilotkit-dwDWYpya.d.cts} +228 -17
- package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.umd.js +1400 -238
- 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 +2442 -552
- 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/copilotkit-props.tsx +4 -2
- package/src/components/toast/toast-provider.tsx +269 -194
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
- package/src/v2/__tests__/utils/test-helpers.tsx +67 -0
- 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 +193 -50
- 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 +253 -149
- 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__/MCPAppsActivityRenderer.e2e.test.tsx +43 -2
- 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-throttle.test.tsx +1003 -0
- package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
- 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 +95 -10
- 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-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-BDNjFNmk.cjs.map +0 -1
- package/dist/copilotkit-BqcyhQjT.d.mts.map +0 -1
- package/dist/copilotkit-DeOzjPsb.mjs.map +0 -1
- package/dist/copilotkit-l-IBF4Xp.d.cts.map +0 -1
- package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
|
-
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
4
4
|
import { HttpAgent } from "@ag-ui/client";
|
|
5
5
|
import { extendTailwindMerge, twMerge } from "tailwind-merge";
|
|
6
|
-
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Volume2, X } from "lucide-react";
|
|
7
|
-
import { COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, createLicenseContextValue, dataToUUID, parseJson, partialJSONParse, randomId, randomUUID } from "@copilotkit/shared";
|
|
6
|
+
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
|
|
7
|
+
import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Slot } from "@radix-ui/react-slot";
|
|
10
10
|
import { cva } from "class-variance-authority";
|
|
@@ -14,19 +14,120 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
|
14
14
|
import { Streamdown } from "streamdown";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
import { createComponent } from "@lit-labs/react";
|
|
17
|
-
import { A2UIProvider, A2UIRenderer, DEFAULT_SURFACE_ID, initializeDefaultCatalog, injectStyles, useA2UIActions, viewerTheme } from "@copilotkit/a2ui-renderer";
|
|
17
|
+
import { A2UIProvider, A2UIRenderer, A2UI_SCHEMA_CONTEXT_DESCRIPTION, DEFAULT_SURFACE_ID, buildCatalogContextValue, extractCatalogComponentSchemas, initializeDefaultCatalog, injectStyles, useA2UIActions, useA2UIError, viewerTheme } from "@copilotkit/a2ui-renderer";
|
|
18
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
19
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
20
|
+
import { createPortal, flushSync } from "react-dom";
|
|
18
21
|
import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
19
|
-
import { merge } from "ts-deepmerge";
|
|
20
|
-
import { flushSync } from "react-dom";
|
|
21
22
|
import ReactMarkdown from "react-markdown";
|
|
22
23
|
|
|
24
|
+
//#region src/v2/lib/slots.tsx
|
|
25
|
+
/**
|
|
26
|
+
* Shallow equality comparison for objects.
|
|
27
|
+
*/
|
|
28
|
+
function shallowEqual(obj1, obj2) {
|
|
29
|
+
const keys1 = Object.keys(obj1);
|
|
30
|
+
const keys2 = Object.keys(obj2);
|
|
31
|
+
if (keys1.length !== keys2.length) return false;
|
|
32
|
+
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
|
|
37
|
+
* class instances, and other exotic objects that happen to have typeof "object".
|
|
38
|
+
*/
|
|
39
|
+
function isPlainObject(obj) {
|
|
40
|
+
return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns the same reference as long as the value is shallowly equal to the
|
|
44
|
+
* previous render's value.
|
|
45
|
+
*
|
|
46
|
+
* - Identical references bail out immediately (O(1)).
|
|
47
|
+
* - Plain objects ({}) are shallow-compared key-by-key.
|
|
48
|
+
* - Arrays, Dates, class instances, functions, and primitives are compared by
|
|
49
|
+
* reference only — shallowEqual is never called on non-plain objects, which
|
|
50
|
+
* avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
|
|
51
|
+
*
|
|
52
|
+
* Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
|
|
53
|
+
* equality check isn't defeated by a new object reference on every render.
|
|
54
|
+
*/
|
|
55
|
+
function useShallowStableRef(value) {
|
|
56
|
+
const ref = useRef(value);
|
|
57
|
+
if (ref.current === value) return ref.current;
|
|
58
|
+
if (isPlainObject(ref.current) && isPlainObject(value)) {
|
|
59
|
+
if (shallowEqual(ref.current, value)) return ref.current;
|
|
60
|
+
}
|
|
61
|
+
ref.current = value;
|
|
62
|
+
return ref.current;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
|
|
66
|
+
*/
|
|
67
|
+
function isReactComponentType(value) {
|
|
68
|
+
if (typeof value === "function") return true;
|
|
69
|
+
if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Internal function to render a slot value as a React element (non-memoized).
|
|
74
|
+
*/
|
|
75
|
+
function renderSlotElement(slot, DefaultComponent, props) {
|
|
76
|
+
if (typeof slot === "string") {
|
|
77
|
+
const existingClassName = props.className;
|
|
78
|
+
return React.createElement(DefaultComponent, {
|
|
79
|
+
...props,
|
|
80
|
+
className: twMerge(existingClassName, slot)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (isReactComponentType(slot)) return React.createElement(slot, props);
|
|
84
|
+
if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
|
|
85
|
+
...props,
|
|
86
|
+
...slot
|
|
87
|
+
});
|
|
88
|
+
return React.createElement(DefaultComponent, props);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Internal memoized wrapper component for renderSlot.
|
|
92
|
+
* Uses forwardRef to support ref forwarding.
|
|
93
|
+
*/
|
|
94
|
+
const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
|
|
95
|
+
const { $slot, $component, ...rest } = props;
|
|
96
|
+
return renderSlotElement($slot, $component, ref !== null ? {
|
|
97
|
+
...rest,
|
|
98
|
+
ref
|
|
99
|
+
} : rest);
|
|
100
|
+
}), (prev, next) => {
|
|
101
|
+
if (prev.$slot !== next.$slot) return false;
|
|
102
|
+
if (prev.$component !== next.$component) return false;
|
|
103
|
+
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
|
|
104
|
+
const { $slot: _ns, $component: _nc, ...nextRest } = next;
|
|
105
|
+
return shallowEqual(prevRest, nextRest);
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* Renders a slot value as a memoized React element.
|
|
109
|
+
* Automatically prevents unnecessary re-renders using shallow prop comparison.
|
|
110
|
+
* Supports ref forwarding.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
|
|
114
|
+
*/
|
|
115
|
+
function renderSlot(slot, DefaultComponent, props) {
|
|
116
|
+
return React.createElement(MemoizedSlotWrapper, {
|
|
117
|
+
...props,
|
|
118
|
+
$slot: slot,
|
|
119
|
+
$component: DefaultComponent
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
23
124
|
//#region src/v2/providers/CopilotChatConfigurationProvider.tsx
|
|
24
125
|
const CopilotChatDefaultLabels = {
|
|
25
126
|
chatInputPlaceholder: "Type a message...",
|
|
26
127
|
chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
|
|
27
128
|
chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
|
|
28
129
|
chatInputToolbarFinishTranscribeButtonLabel: "Finish",
|
|
29
|
-
chatInputToolbarAddButtonLabel: "Add
|
|
130
|
+
chatInputToolbarAddButtonLabel: "Add attachments",
|
|
30
131
|
chatInputToolbarToolsButtonLabel: "Tools",
|
|
31
132
|
assistantMessageToolbarCopyCodeLabel: "Copy",
|
|
32
133
|
assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
|
|
@@ -46,11 +147,12 @@ const CopilotChatDefaultLabels = {
|
|
|
46
147
|
const CopilotChatConfiguration = createContext(null);
|
|
47
148
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
|
|
48
149
|
const parentConfig = useContext(CopilotChatConfiguration);
|
|
150
|
+
const stableLabels = useShallowStableRef(labels);
|
|
49
151
|
const mergedLabels = useMemo(() => ({
|
|
50
152
|
...CopilotChatDefaultLabels,
|
|
51
153
|
...parentConfig?.labels ?? {},
|
|
52
|
-
...
|
|
53
|
-
}), [
|
|
154
|
+
...stableLabels ?? {}
|
|
155
|
+
}), [stableLabels, parentConfig?.labels]);
|
|
54
156
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
55
157
|
const resolvedThreadId = useMemo(() => {
|
|
56
158
|
if (threadId) return threadId;
|
|
@@ -473,82 +575,11 @@ const CopilotChatAudioRecorder = forwardRef((props, ref) => {
|
|
|
473
575
|
});
|
|
474
576
|
CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
|
|
475
577
|
|
|
476
|
-
//#endregion
|
|
477
|
-
//#region src/v2/lib/slots.tsx
|
|
478
|
-
/**
|
|
479
|
-
* Shallow equality comparison for objects.
|
|
480
|
-
*/
|
|
481
|
-
function shallowEqual(obj1, obj2) {
|
|
482
|
-
const keys1 = Object.keys(obj1);
|
|
483
|
-
const keys2 = Object.keys(obj2);
|
|
484
|
-
if (keys1.length !== keys2.length) return false;
|
|
485
|
-
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
|
|
486
|
-
return true;
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
|
|
490
|
-
*/
|
|
491
|
-
function isReactComponentType(value) {
|
|
492
|
-
if (typeof value === "function") return true;
|
|
493
|
-
if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Internal function to render a slot value as a React element (non-memoized).
|
|
498
|
-
*/
|
|
499
|
-
function renderSlotElement(slot, DefaultComponent, props) {
|
|
500
|
-
if (typeof slot === "string") {
|
|
501
|
-
const existingClassName = props.className;
|
|
502
|
-
return React.createElement(DefaultComponent, {
|
|
503
|
-
...props,
|
|
504
|
-
className: twMerge(existingClassName, slot)
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
if (isReactComponentType(slot)) return React.createElement(slot, props);
|
|
508
|
-
if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
|
|
509
|
-
...props,
|
|
510
|
-
...slot
|
|
511
|
-
});
|
|
512
|
-
return React.createElement(DefaultComponent, props);
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Internal memoized wrapper component for renderSlot.
|
|
516
|
-
* Uses forwardRef to support ref forwarding.
|
|
517
|
-
*/
|
|
518
|
-
const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
|
|
519
|
-
const { $slot, $component, ...rest } = props;
|
|
520
|
-
return renderSlotElement($slot, $component, ref !== null ? {
|
|
521
|
-
...rest,
|
|
522
|
-
ref
|
|
523
|
-
} : rest);
|
|
524
|
-
}), (prev, next) => {
|
|
525
|
-
if (prev.$slot !== next.$slot) return false;
|
|
526
|
-
if (prev.$component !== next.$component) return false;
|
|
527
|
-
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
|
|
528
|
-
const { $slot: _ns, $component: _nc, ...nextRest } = next;
|
|
529
|
-
return shallowEqual(prevRest, nextRest);
|
|
530
|
-
});
|
|
531
|
-
/**
|
|
532
|
-
* Renders a slot value as a memoized React element.
|
|
533
|
-
* Automatically prevents unnecessary re-renders using shallow prop comparison.
|
|
534
|
-
* Supports ref forwarding.
|
|
535
|
-
*
|
|
536
|
-
* @example
|
|
537
|
-
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
|
|
538
|
-
*/
|
|
539
|
-
function renderSlot(slot, DefaultComponent, props) {
|
|
540
|
-
return React.createElement(MemoizedSlotWrapper, {
|
|
541
|
-
...props,
|
|
542
|
-
$slot: slot,
|
|
543
|
-
$component: DefaultComponent
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
|
|
547
578
|
//#endregion
|
|
548
579
|
//#region src/v2/components/chat/CopilotChatInput.tsx
|
|
549
580
|
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
|
|
550
581
|
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
|
|
551
|
-
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus =
|
|
582
|
+
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
|
|
552
583
|
const isControlled = value !== void 0;
|
|
553
584
|
const [internalValue, setInternalValue] = useState(() => value ?? "");
|
|
554
585
|
useEffect(() => {
|
|
@@ -577,6 +608,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
577
608
|
paddingLeft: 0,
|
|
578
609
|
paddingRight: 0
|
|
579
610
|
});
|
|
611
|
+
const containerCacheRef = useRef(null);
|
|
580
612
|
const commandItems = useMemo(() => {
|
|
581
613
|
const entries = [];
|
|
582
614
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -621,7 +653,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
621
653
|
previousModalStateRef.current = config?.isModalOpen;
|
|
622
654
|
return;
|
|
623
655
|
}
|
|
624
|
-
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus();
|
|
656
|
+
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
|
|
625
657
|
previousModalStateRef.current = config?.isModalOpen;
|
|
626
658
|
}, [config?.isModalOpen, autoFocus]);
|
|
627
659
|
useEffect(() => {
|
|
@@ -864,6 +896,25 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
864
896
|
return nextLayout;
|
|
865
897
|
});
|
|
866
898
|
}, []);
|
|
899
|
+
const updateContainerCache = useCallback(() => {
|
|
900
|
+
const grid = gridRef.current;
|
|
901
|
+
const addContainer = addButtonContainerRef.current;
|
|
902
|
+
const actionsContainer = actionsContainerRef.current;
|
|
903
|
+
if (!grid || !addContainer || !actionsContainer) return null;
|
|
904
|
+
const gridStyles = window.getComputedStyle(grid);
|
|
905
|
+
const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
|
|
906
|
+
const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
|
|
907
|
+
const columnGap = parseFloat(gridStyles.columnGap) || 0;
|
|
908
|
+
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
909
|
+
if (gridAvailableWidth <= 0) return null;
|
|
910
|
+
const addWidth = addContainer.getBoundingClientRect().width;
|
|
911
|
+
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
912
|
+
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
|
|
913
|
+
if (compactWidth <= 0) return null;
|
|
914
|
+
const result = { compactWidth };
|
|
915
|
+
containerCacheRef.current = result;
|
|
916
|
+
return result;
|
|
917
|
+
}, []);
|
|
867
918
|
const evaluateLayout = useCallback(() => {
|
|
868
919
|
if (mode !== "input") {
|
|
869
920
|
updateLayout("compact");
|
|
@@ -889,31 +940,31 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
889
940
|
const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
|
|
890
941
|
let shouldExpand = hasExplicitBreak || renderedMultiline;
|
|
891
942
|
if (!shouldExpand) {
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
897
|
-
if (gridAvailableWidth > 0) {
|
|
898
|
-
const addWidth = addContainer.getBoundingClientRect().width;
|
|
899
|
-
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
900
|
-
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
|
|
901
|
-
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
|
|
902
|
-
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
|
|
903
|
-
const context = canvas.getContext("2d");
|
|
904
|
-
if (context) {
|
|
943
|
+
const cache = containerCacheRef.current ?? updateContainerCache();
|
|
944
|
+
if (cache && cache.compactWidth > 0) {
|
|
945
|
+
const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
|
|
946
|
+
if (compactInnerWidth > 0) {
|
|
905
947
|
const textareaStyles = window.getComputedStyle(textarea);
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
let longestWidth = 0;
|
|
911
|
-
for (const line of lines) {
|
|
912
|
-
const metrics = context.measureText(line || " ");
|
|
913
|
-
if (metrics.width > longestWidth) longestWidth = metrics.width;
|
|
914
|
-
}
|
|
915
|
-
if (longestWidth > compactInnerWidth) shouldExpand = true;
|
|
948
|
+
let font = textareaStyles.font;
|
|
949
|
+
if (!font) {
|
|
950
|
+
const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
|
|
951
|
+
if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
|
|
916
952
|
}
|
|
953
|
+
if (font?.trim()) {
|
|
954
|
+
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
|
|
955
|
+
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
|
|
956
|
+
const context = canvas.getContext("2d");
|
|
957
|
+
if (context) {
|
|
958
|
+
context.font = font;
|
|
959
|
+
const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
|
|
960
|
+
let longestWidth = 0;
|
|
961
|
+
for (const line of lines) {
|
|
962
|
+
const metrics = context.measureText(line || " ");
|
|
963
|
+
if (metrics.width > longestWidth) longestWidth = metrics.width;
|
|
964
|
+
}
|
|
965
|
+
if (longestWidth > compactInnerWidth) shouldExpand = true;
|
|
966
|
+
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
|
|
967
|
+
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] Could not resolve textarea font for layout measurement. Text-width-based expansion will be skipped until the next evaluation.");
|
|
917
968
|
}
|
|
918
969
|
}
|
|
919
970
|
}
|
|
@@ -923,6 +974,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
923
974
|
ensureMeasurements,
|
|
924
975
|
mode,
|
|
925
976
|
resolvedValue,
|
|
977
|
+
updateContainerCache,
|
|
926
978
|
updateLayout
|
|
927
979
|
]);
|
|
928
980
|
useLayoutEffect(() => {
|
|
@@ -935,11 +987,17 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
935
987
|
const addContainer = addButtonContainerRef.current;
|
|
936
988
|
const actionsContainer = actionsContainerRef.current;
|
|
937
989
|
if (!textarea || !grid || !addContainer || !actionsContainer) return;
|
|
938
|
-
const
|
|
990
|
+
const containerTargets = new Set([
|
|
991
|
+
grid,
|
|
992
|
+
addContainer,
|
|
993
|
+
actionsContainer
|
|
994
|
+
]);
|
|
995
|
+
const scheduleEvaluation = (invalidateCache) => {
|
|
939
996
|
if (ignoreResizeRef.current) {
|
|
940
997
|
ignoreResizeRef.current = false;
|
|
941
998
|
return;
|
|
942
999
|
}
|
|
1000
|
+
if (invalidateCache) containerCacheRef.current = null;
|
|
943
1001
|
if (typeof window === "undefined") {
|
|
944
1002
|
evaluateLayout();
|
|
945
1003
|
return;
|
|
@@ -950,8 +1008,13 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
950
1008
|
evaluateLayout();
|
|
951
1009
|
});
|
|
952
1010
|
};
|
|
953
|
-
const observer = new ResizeObserver(() => {
|
|
954
|
-
|
|
1011
|
+
const observer = new ResizeObserver((entries) => {
|
|
1012
|
+
let shouldInvalidate = false;
|
|
1013
|
+
for (const entry of entries) if (containerTargets.has(entry.target)) {
|
|
1014
|
+
shouldInvalidate = true;
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
scheduleEvaluation(shouldInvalidate);
|
|
955
1018
|
});
|
|
956
1019
|
observer.observe(grid);
|
|
957
1020
|
observer.observe(addContainer);
|
|
@@ -1096,6 +1159,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1096
1159
|
});
|
|
1097
1160
|
_CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
|
|
1098
1161
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1162
|
+
const [mounted, setMounted] = useState(false);
|
|
1163
|
+
useEffect(() => setMounted(true), []);
|
|
1099
1164
|
const menuItems = useMemo(() => {
|
|
1100
1165
|
const items = [];
|
|
1101
1166
|
if (onAddFile) items.push({
|
|
@@ -1126,26 +1191,28 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1126
1191
|
}), []);
|
|
1127
1192
|
const hasMenuItems = menuItems.length > 0;
|
|
1128
1193
|
const isDisabled = disabled || !hasMenuItems;
|
|
1194
|
+
const button = /* @__PURE__ */ jsx(Button, {
|
|
1195
|
+
type: "button",
|
|
1196
|
+
"data-testid": "copilot-add-menu-button",
|
|
1197
|
+
variant: "chatInputToolbarSecondary",
|
|
1198
|
+
size: "chatInputToolbarIcon",
|
|
1199
|
+
className: twMerge("cpk:ml-1", className),
|
|
1200
|
+
disabled: isDisabled,
|
|
1201
|
+
...props,
|
|
1202
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
|
|
1203
|
+
});
|
|
1204
|
+
if (!mounted) return button;
|
|
1129
1205
|
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1130
1206
|
asChild: true,
|
|
1131
1207
|
children: /* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
1132
1208
|
asChild: true,
|
|
1133
|
-
children:
|
|
1134
|
-
type: "button",
|
|
1135
|
-
"data-testid": "copilot-add-menu-button",
|
|
1136
|
-
variant: "chatInputToolbarSecondary",
|
|
1137
|
-
size: "chatInputToolbarIcon",
|
|
1138
|
-
className: twMerge("cpk:ml-1", className),
|
|
1139
|
-
disabled: isDisabled,
|
|
1140
|
-
...props,
|
|
1141
|
-
children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
|
|
1142
|
-
})
|
|
1209
|
+
children: button
|
|
1143
1210
|
})
|
|
1144
1211
|
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
1145
1212
|
side: "bottom",
|
|
1146
1213
|
children: /* @__PURE__ */ jsxs("p", {
|
|
1147
1214
|
className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
|
|
1148
|
-
children: [/* @__PURE__ */ jsx("span", { children: "Add
|
|
1215
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Add attachments" }), /* @__PURE__ */ jsx("code", {
|
|
1149
1216
|
className: "cpk:rounded cpk:bg-[#4a4a4a] cpk:px-1 cpk:py-[1px] cpk:font-mono cpk:text-[11px] cpk:text-white cpk:dark:bg-[#e0e0e0] cpk:dark:text-black",
|
|
1150
1217
|
children: "/"
|
|
1151
1218
|
})]
|
|
@@ -1161,21 +1228,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1161
1228
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1162
1229
|
useImperativeHandle(ref, () => internalTextareaRef.current);
|
|
1163
1230
|
useEffect(() => {
|
|
1164
|
-
|
|
1165
|
-
if (!textarea) return;
|
|
1166
|
-
const handleFocus = () => {
|
|
1167
|
-
setTimeout(() => {
|
|
1168
|
-
textarea.scrollIntoView({
|
|
1169
|
-
behavior: "smooth",
|
|
1170
|
-
block: "nearest"
|
|
1171
|
-
});
|
|
1172
|
-
}, 300);
|
|
1173
|
-
};
|
|
1174
|
-
textarea.addEventListener("focus", handleFocus);
|
|
1175
|
-
return () => textarea.removeEventListener("focus", handleFocus);
|
|
1176
|
-
}, []);
|
|
1177
|
-
useEffect(() => {
|
|
1178
|
-
if (autoFocus) internalTextareaRef.current?.focus();
|
|
1231
|
+
if (autoFocus) internalTextareaRef.current?.focus({ preventScroll: true });
|
|
1179
1232
|
}, [autoFocus]);
|
|
1180
1233
|
return /* @__PURE__ */ jsx("textarea", {
|
|
1181
1234
|
ref: internalTextareaRef,
|
|
@@ -1882,8 +1935,411 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
|
|
|
1882
1935
|
});
|
|
1883
1936
|
};
|
|
1884
1937
|
|
|
1938
|
+
//#endregion
|
|
1939
|
+
//#region src/v2/providers/SandboxFunctionsContext.ts
|
|
1940
|
+
const SandboxFunctionsContext = createContext([]);
|
|
1941
|
+
function useSandboxFunctions() {
|
|
1942
|
+
return useContext(SandboxFunctionsContext);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
//#endregion
|
|
1946
|
+
//#region src/v2/lib/processPartialHtml.ts
|
|
1947
|
+
/**
|
|
1948
|
+
* Extracts all complete `<style>` blocks from the raw HTML.
|
|
1949
|
+
* Returns the concatenated style tags, suitable for injection into `<head>`.
|
|
1950
|
+
*/
|
|
1951
|
+
function extractCompleteStyles(html) {
|
|
1952
|
+
const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
|
|
1953
|
+
return matches ? matches.join("") : "";
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Processes raw accumulated HTML for safe preview via innerHTML injection.
|
|
1957
|
+
* Pure function, no DOM dependencies.
|
|
1958
|
+
*
|
|
1959
|
+
* Pipeline (order matters):
|
|
1960
|
+
* 1. Strip incomplete tag at end
|
|
1961
|
+
* 2. Strip complete <style>, <script>, and <head> blocks
|
|
1962
|
+
* 3. Strip incomplete <style>/<script>/<head> blocks
|
|
1963
|
+
* 4. Strip incomplete HTML entities
|
|
1964
|
+
* 5. Extract body content (or use full string if no <body>)
|
|
1965
|
+
*/
|
|
1966
|
+
function processPartialHtml(html) {
|
|
1967
|
+
let result = html;
|
|
1968
|
+
result = result.replace(/<[^>]*$/, "");
|
|
1969
|
+
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
1970
|
+
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
|
|
1971
|
+
result = result.replace(/&[a-zA-Z0-9#]*$/, "");
|
|
1972
|
+
const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
|
|
1973
|
+
if (bodyMatch) {
|
|
1974
|
+
result = bodyMatch[1];
|
|
1975
|
+
result = result.replace(/<\/body>[\s\S]*/i, "");
|
|
1976
|
+
}
|
|
1977
|
+
return result;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
//#endregion
|
|
1981
|
+
//#region src/v2/components/OpenGenerativeUIRenderer.tsx
|
|
1982
|
+
const OpenGenerativeUIActivityType = "open-generative-ui";
|
|
1983
|
+
const OpenGenerativeUIContentSchema = z.object({
|
|
1984
|
+
initialHeight: z.number().optional(),
|
|
1985
|
+
generating: z.boolean().optional(),
|
|
1986
|
+
css: z.string().optional(),
|
|
1987
|
+
cssComplete: z.boolean().optional(),
|
|
1988
|
+
html: z.array(z.string()).optional(),
|
|
1989
|
+
htmlComplete: z.boolean().optional(),
|
|
1990
|
+
jsFunctions: z.string().optional(),
|
|
1991
|
+
jsFunctionsComplete: z.boolean().optional(),
|
|
1992
|
+
jsExpressions: z.array(z.string()).optional(),
|
|
1993
|
+
jsExpressionsComplete: z.boolean().optional()
|
|
1994
|
+
});
|
|
1995
|
+
/**
|
|
1996
|
+
* Schema for the generateSandboxedUi tool call arguments.
|
|
1997
|
+
* Used by the frontend tool renderer to display placeholder messages.
|
|
1998
|
+
*/
|
|
1999
|
+
const GenerateSandboxedUiArgsSchema = z.object({
|
|
2000
|
+
initialHeight: z.number().optional(),
|
|
2001
|
+
placeholderMessages: z.array(z.string()).optional(),
|
|
2002
|
+
css: z.string().optional(),
|
|
2003
|
+
html: z.string().optional(),
|
|
2004
|
+
jsFunctions: z.string().optional(),
|
|
2005
|
+
jsExpressions: z.array(z.string()).optional()
|
|
2006
|
+
});
|
|
2007
|
+
const THROTTLE_MS = 1e3;
|
|
2008
|
+
/**
|
|
2009
|
+
* Returns true when the inner component should re-render immediately
|
|
2010
|
+
* (no throttle delay).
|
|
2011
|
+
*/
|
|
2012
|
+
function shouldFlushImmediately(prev, next) {
|
|
2013
|
+
if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
|
|
2014
|
+
if (next.htmlComplete) return true;
|
|
2015
|
+
if (next.generating === false) return true;
|
|
2016
|
+
if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
|
|
2017
|
+
if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
|
|
2018
|
+
if (next.html?.length && (!prev || !prev.html?.length)) return true;
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Outer wrapper — absorbs every parent re-render but only forwards
|
|
2023
|
+
* throttled content snapshots to the memoized inner component.
|
|
2024
|
+
*/
|
|
2025
|
+
const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
|
|
2026
|
+
const latestContentRef = useRef(content);
|
|
2027
|
+
latestContentRef.current = content;
|
|
2028
|
+
const [throttledContent, setThrottledContent] = useState(content);
|
|
2029
|
+
const throttledContentRef = useRef(throttledContent);
|
|
2030
|
+
const timerRef = useRef(null);
|
|
2031
|
+
if (throttledContentRef.current !== content) {
|
|
2032
|
+
if (shouldFlushImmediately(throttledContentRef.current, content)) {
|
|
2033
|
+
if (timerRef.current !== null) {
|
|
2034
|
+
clearTimeout(timerRef.current);
|
|
2035
|
+
timerRef.current = null;
|
|
2036
|
+
}
|
|
2037
|
+
throttledContentRef.current = content;
|
|
2038
|
+
setThrottledContent(content);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
const flush = useCallback(() => {
|
|
2042
|
+
timerRef.current = null;
|
|
2043
|
+
const latest = latestContentRef.current;
|
|
2044
|
+
throttledContentRef.current = latest;
|
|
2045
|
+
setThrottledContent(latest);
|
|
2046
|
+
}, []);
|
|
2047
|
+
useEffect(() => {
|
|
2048
|
+
if (throttledContentRef.current === content) return;
|
|
2049
|
+
if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
|
|
2050
|
+
}, [content, flush]);
|
|
2051
|
+
useEffect(() => {
|
|
2052
|
+
return () => {
|
|
2053
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
2054
|
+
};
|
|
2055
|
+
}, []);
|
|
2056
|
+
return /* @__PURE__ */ jsx(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
|
|
2057
|
+
};
|
|
2058
|
+
function ensureHead(html) {
|
|
2059
|
+
if (/<head[\s>]/i.test(html)) return html;
|
|
2060
|
+
return `<head></head>${html}`;
|
|
2061
|
+
}
|
|
2062
|
+
function injectCssIntoHtml(html, css) {
|
|
2063
|
+
const headCloseIdx = html.indexOf("</head>");
|
|
2064
|
+
if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
|
|
2065
|
+
return `<head><style>${css}</style></head>${html}`;
|
|
2066
|
+
}
|
|
2067
|
+
const OpenGenerativeUIActivityRendererInner = React.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
|
|
2068
|
+
const initialHeight = content.initialHeight ?? 200;
|
|
2069
|
+
const [autoHeight, setAutoHeight] = useState(null);
|
|
2070
|
+
const sandboxFunctions = useSandboxFunctions();
|
|
2071
|
+
const localApi = useMemo(() => {
|
|
2072
|
+
const api = {};
|
|
2073
|
+
for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
|
|
2074
|
+
return api;
|
|
2075
|
+
}, [sandboxFunctions]);
|
|
2076
|
+
const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
|
|
2077
|
+
const css = content.cssComplete ? content.css : void 0;
|
|
2078
|
+
const cssReady = !!content.cssComplete;
|
|
2079
|
+
const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
|
|
2080
|
+
const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
|
|
2081
|
+
const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
|
|
2082
|
+
const hasPreview = cssReady && !!previewBody?.trim();
|
|
2083
|
+
const hasVisibleSandbox = !!fullHtml || hasPreview;
|
|
2084
|
+
const containerRef = useRef(null);
|
|
2085
|
+
const sandboxRef = useRef(null);
|
|
2086
|
+
const previewSandboxRef = useRef(null);
|
|
2087
|
+
const previewReadyRef = useRef(false);
|
|
2088
|
+
const sandboxReadyRef = useRef(false);
|
|
2089
|
+
const executedIndexRef = useRef(0);
|
|
2090
|
+
const pendingQueueRef = useRef([]);
|
|
2091
|
+
const jsFunctionsInjectedRef = useRef(false);
|
|
2092
|
+
useEffect(() => {
|
|
2093
|
+
const container = containerRef.current;
|
|
2094
|
+
if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
|
|
2095
|
+
let cancelled = false;
|
|
2096
|
+
import("@jetbrains/websandbox").then((mod) => {
|
|
2097
|
+
if (cancelled) return;
|
|
2098
|
+
const sandbox = (mod.default?.default ?? mod.default).create({}, {
|
|
2099
|
+
frameContainer: container,
|
|
2100
|
+
frameContent: "<head></head><body></body>",
|
|
2101
|
+
allowAdditionalAttributes: ""
|
|
2102
|
+
});
|
|
2103
|
+
previewSandboxRef.current = sandbox;
|
|
2104
|
+
sandbox.iframe.style.width = "100%";
|
|
2105
|
+
sandbox.iframe.style.height = "100%";
|
|
2106
|
+
sandbox.iframe.style.border = "none";
|
|
2107
|
+
sandbox.iframe.style.backgroundColor = "transparent";
|
|
2108
|
+
sandbox.promise.then(() => {
|
|
2109
|
+
if (cancelled) return;
|
|
2110
|
+
previewReadyRef.current = true;
|
|
2111
|
+
sandbox.run(`
|
|
2112
|
+
var s = document.createElement('style');
|
|
2113
|
+
s.textContent = 'html, body { overflow: hidden !important; }';
|
|
2114
|
+
document.head.appendChild(s);
|
|
2115
|
+
`);
|
|
2116
|
+
const headParts = [];
|
|
2117
|
+
if (css) headParts.push(`<style>${css}</style>`);
|
|
2118
|
+
if (previewStyles) headParts.push(previewStyles);
|
|
2119
|
+
if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
|
|
2120
|
+
if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
|
|
2121
|
+
});
|
|
2122
|
+
}).catch((err) => {
|
|
2123
|
+
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
|
|
2124
|
+
});
|
|
2125
|
+
return () => {
|
|
2126
|
+
cancelled = true;
|
|
2127
|
+
};
|
|
2128
|
+
}, [hasPreview, fullHtml]);
|
|
2129
|
+
useEffect(() => {
|
|
2130
|
+
if (!previewSandboxRef.current || !previewReadyRef.current) return;
|
|
2131
|
+
const headParts = [];
|
|
2132
|
+
if (css) headParts.push(`<style>${css}</style>`);
|
|
2133
|
+
if (previewStyles) headParts.push(previewStyles);
|
|
2134
|
+
if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
|
|
2135
|
+
if (!previewBody) return;
|
|
2136
|
+
previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
|
|
2137
|
+
}, [
|
|
2138
|
+
previewBody,
|
|
2139
|
+
previewStyles,
|
|
2140
|
+
css
|
|
2141
|
+
]);
|
|
2142
|
+
useEffect(() => {
|
|
2143
|
+
const container = containerRef.current;
|
|
2144
|
+
if (!container || !fullHtml) return;
|
|
2145
|
+
if (previewSandboxRef.current) {
|
|
2146
|
+
previewSandboxRef.current.destroy();
|
|
2147
|
+
previewSandboxRef.current = null;
|
|
2148
|
+
previewReadyRef.current = false;
|
|
2149
|
+
}
|
|
2150
|
+
let cancelled = false;
|
|
2151
|
+
executedIndexRef.current = 0;
|
|
2152
|
+
jsFunctionsInjectedRef.current = false;
|
|
2153
|
+
sandboxReadyRef.current = false;
|
|
2154
|
+
pendingQueueRef.current = [];
|
|
2155
|
+
const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
|
|
2156
|
+
import("@jetbrains/websandbox").then((mod) => {
|
|
2157
|
+
if (cancelled) return;
|
|
2158
|
+
const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
|
|
2159
|
+
frameContainer: container,
|
|
2160
|
+
frameContent: ensureHead(htmlContent),
|
|
2161
|
+
allowAdditionalAttributes: ""
|
|
2162
|
+
});
|
|
2163
|
+
sandboxRef.current = sandbox;
|
|
2164
|
+
sandbox.iframe.style.width = "100%";
|
|
2165
|
+
sandbox.iframe.style.height = "100%";
|
|
2166
|
+
sandbox.iframe.style.border = "none";
|
|
2167
|
+
sandbox.iframe.style.backgroundColor = "transparent";
|
|
2168
|
+
sandbox.promise.then(() => {
|
|
2169
|
+
if (cancelled) return;
|
|
2170
|
+
sandboxReadyRef.current = true;
|
|
2171
|
+
sandbox.run(`
|
|
2172
|
+
var s = document.createElement('style');
|
|
2173
|
+
s.textContent = 'html, body { overflow: hidden !important; }';
|
|
2174
|
+
document.head.appendChild(s);
|
|
2175
|
+
`);
|
|
2176
|
+
const queue = pendingQueueRef.current;
|
|
2177
|
+
pendingQueueRef.current = [];
|
|
2178
|
+
for (const code of queue) sandbox.run(code);
|
|
2179
|
+
});
|
|
2180
|
+
}).catch((err) => {
|
|
2181
|
+
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
|
|
2182
|
+
});
|
|
2183
|
+
return () => {
|
|
2184
|
+
cancelled = true;
|
|
2185
|
+
if (previewSandboxRef.current) {
|
|
2186
|
+
previewSandboxRef.current.destroy();
|
|
2187
|
+
previewSandboxRef.current = null;
|
|
2188
|
+
previewReadyRef.current = false;
|
|
2189
|
+
}
|
|
2190
|
+
if (sandboxRef.current) {
|
|
2191
|
+
sandboxRef.current.destroy();
|
|
2192
|
+
sandboxRef.current = null;
|
|
2193
|
+
}
|
|
2194
|
+
sandboxReadyRef.current = false;
|
|
2195
|
+
setAutoHeight(null);
|
|
2196
|
+
};
|
|
2197
|
+
}, [
|
|
2198
|
+
fullHtml,
|
|
2199
|
+
css,
|
|
2200
|
+
localApi
|
|
2201
|
+
]);
|
|
2202
|
+
useEffect(() => {
|
|
2203
|
+
if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
|
|
2204
|
+
jsFunctionsInjectedRef.current = true;
|
|
2205
|
+
const sandbox = sandboxRef.current;
|
|
2206
|
+
if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
|
|
2207
|
+
else pendingQueueRef.current.push(content.jsFunctions);
|
|
2208
|
+
}, [content.jsFunctions]);
|
|
2209
|
+
useEffect(() => {
|
|
2210
|
+
const expressions = content.jsExpressions;
|
|
2211
|
+
if (!expressions || expressions.length === 0) return;
|
|
2212
|
+
const startIndex = executedIndexRef.current;
|
|
2213
|
+
if (startIndex >= expressions.length) return;
|
|
2214
|
+
const newExprs = expressions.slice(startIndex);
|
|
2215
|
+
executedIndexRef.current = expressions.length;
|
|
2216
|
+
const sandbox = sandboxRef.current;
|
|
2217
|
+
if (sandboxReadyRef.current && sandbox) (async () => {
|
|
2218
|
+
for (const expr of newExprs) await sandbox.run(expr);
|
|
2219
|
+
})();
|
|
2220
|
+
else pendingQueueRef.current.push(...newExprs);
|
|
2221
|
+
}, [content.jsExpressions?.length]);
|
|
2222
|
+
const generationDone = content.generating === false;
|
|
2223
|
+
useEffect(() => {
|
|
2224
|
+
const sandbox = sandboxRef.current;
|
|
2225
|
+
if (!generationDone || !sandbox) return;
|
|
2226
|
+
let handled = false;
|
|
2227
|
+
const onMessage = (e) => {
|
|
2228
|
+
if (handled) return;
|
|
2229
|
+
if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
|
|
2230
|
+
handled = true;
|
|
2231
|
+
setAutoHeight(e.data.height);
|
|
2232
|
+
window.removeEventListener("message", onMessage);
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
window.addEventListener("message", onMessage);
|
|
2236
|
+
const measureOnce = `
|
|
2237
|
+
(function() {
|
|
2238
|
+
var s = document.createElement('style');
|
|
2239
|
+
s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
|
|
2240
|
+
document.head.appendChild(s);
|
|
2241
|
+
var h = document.body.scrollHeight;
|
|
2242
|
+
var cs = getComputedStyle(document.body);
|
|
2243
|
+
h += parseFloat(cs.marginTop) || 0;
|
|
2244
|
+
h += parseFloat(cs.marginBottom) || 0;
|
|
2245
|
+
s.remove();
|
|
2246
|
+
parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
|
|
2247
|
+
})();
|
|
2248
|
+
`;
|
|
2249
|
+
if (sandboxReadyRef.current) sandbox.run(measureOnce);
|
|
2250
|
+
else pendingQueueRef.current.push(measureOnce);
|
|
2251
|
+
return () => {
|
|
2252
|
+
window.removeEventListener("message", onMessage);
|
|
2253
|
+
};
|
|
2254
|
+
}, [generationDone]);
|
|
2255
|
+
const height = autoHeight ?? initialHeight;
|
|
2256
|
+
const isGenerating = content.generating !== false;
|
|
2257
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2258
|
+
ref: containerRef,
|
|
2259
|
+
style: {
|
|
2260
|
+
position: "relative",
|
|
2261
|
+
width: "100%",
|
|
2262
|
+
height: `${height}px`,
|
|
2263
|
+
borderRadius: "8px",
|
|
2264
|
+
backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
|
|
2265
|
+
border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
|
|
2266
|
+
display: hasVisibleSandbox ? "block" : "flex",
|
|
2267
|
+
alignItems: hasVisibleSandbox ? void 0 : "center",
|
|
2268
|
+
justifyContent: hasVisibleSandbox ? void 0 : "center",
|
|
2269
|
+
overflow: "hidden"
|
|
2270
|
+
},
|
|
2271
|
+
children: isGenerating && /* @__PURE__ */ jsxs("div", {
|
|
2272
|
+
style: {
|
|
2273
|
+
position: "absolute",
|
|
2274
|
+
inset: 0,
|
|
2275
|
+
zIndex: 10,
|
|
2276
|
+
pointerEvents: "all",
|
|
2277
|
+
backgroundColor: "rgba(255, 255, 255, 0.5)",
|
|
2278
|
+
display: "flex",
|
|
2279
|
+
alignItems: "center",
|
|
2280
|
+
justifyContent: "center"
|
|
2281
|
+
},
|
|
2282
|
+
children: [/* @__PURE__ */ jsxs("svg", {
|
|
2283
|
+
width: "48",
|
|
2284
|
+
height: "48",
|
|
2285
|
+
viewBox: "0 0 24 24",
|
|
2286
|
+
fill: "none",
|
|
2287
|
+
style: { animation: "ck-spin 1s linear infinite" },
|
|
2288
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
2289
|
+
cx: "12",
|
|
2290
|
+
cy: "12",
|
|
2291
|
+
r: "10",
|
|
2292
|
+
stroke: "#e0e0e0",
|
|
2293
|
+
strokeWidth: "3"
|
|
2294
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
2295
|
+
d: "M12 2a10 10 0 0 1 10 10",
|
|
2296
|
+
stroke: "#999",
|
|
2297
|
+
strokeWidth: "3",
|
|
2298
|
+
strokeLinecap: "round"
|
|
2299
|
+
})]
|
|
2300
|
+
}), /* @__PURE__ */ jsx("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
|
|
2301
|
+
})
|
|
2302
|
+
});
|
|
2303
|
+
}, (prev, next) => prev.content === next.content);
|
|
2304
|
+
/**
|
|
2305
|
+
* Frontend tool renderer for generateSandboxedUi.
|
|
2306
|
+
* Displays placeholder messages while the UI is being generated.
|
|
2307
|
+
*/
|
|
2308
|
+
const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
|
|
2309
|
+
const [visibleMessageIndex, setVisibleMessageIndex] = useState(0);
|
|
2310
|
+
const prevMessageCountRef = useRef(0);
|
|
2311
|
+
const messages = props.args.placeholderMessages;
|
|
2312
|
+
useEffect(() => {
|
|
2313
|
+
if (!messages || messages.length === 0) return;
|
|
2314
|
+
if (messages.length !== prevMessageCountRef.current) {
|
|
2315
|
+
prevMessageCountRef.current = messages.length;
|
|
2316
|
+
setVisibleMessageIndex(messages.length - 1);
|
|
2317
|
+
}
|
|
2318
|
+
if (props.status === ToolCallStatus.Complete) return;
|
|
2319
|
+
const timer = setInterval(() => {
|
|
2320
|
+
setVisibleMessageIndex((i) => (i + 1) % messages.length);
|
|
2321
|
+
}, 5e3);
|
|
2322
|
+
return () => clearInterval(timer);
|
|
2323
|
+
}, [messages?.length, props.status]);
|
|
2324
|
+
if (props.status === ToolCallStatus.Complete) return null;
|
|
2325
|
+
if (!messages || messages.length === 0) return null;
|
|
2326
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2327
|
+
style: {
|
|
2328
|
+
padding: "8px 12px",
|
|
2329
|
+
color: "#999",
|
|
2330
|
+
fontSize: "14px"
|
|
2331
|
+
},
|
|
2332
|
+
children: messages[visibleMessageIndex] ?? messages[0]
|
|
2333
|
+
});
|
|
2334
|
+
};
|
|
2335
|
+
|
|
1885
2336
|
//#endregion
|
|
1886
2337
|
//#region src/v2/a2ui/A2UIMessageRenderer.tsx
|
|
2338
|
+
/**
|
|
2339
|
+
* The container key used to wrap A2UI operations for explicit detection.
|
|
2340
|
+
* Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
|
|
2341
|
+
*/
|
|
2342
|
+
const A2UI_OPERATIONS_KEY = "a2ui_operations";
|
|
1887
2343
|
let initialized = false;
|
|
1888
2344
|
function ensureInitialized() {
|
|
1889
2345
|
if (!initialized) {
|
|
@@ -1893,25 +2349,23 @@ function ensureInitialized() {
|
|
|
1893
2349
|
}
|
|
1894
2350
|
}
|
|
1895
2351
|
function createA2UIMessageRenderer(options) {
|
|
1896
|
-
const { theme } = options;
|
|
2352
|
+
const { theme, catalog, loadingComponent } = options;
|
|
1897
2353
|
return {
|
|
1898
2354
|
activityType: "a2ui-surface",
|
|
1899
2355
|
content: z.any(),
|
|
1900
2356
|
render: ({ content, agent }) => {
|
|
1901
2357
|
ensureInitialized();
|
|
1902
2358
|
const [operations, setOperations] = useState([]);
|
|
1903
|
-
const lastSignatureRef = useRef(null);
|
|
1904
2359
|
const { copilotkit } = useCopilotKit();
|
|
2360
|
+
const lastContentRef = useRef(null);
|
|
1905
2361
|
useEffect(() => {
|
|
1906
|
-
if (
|
|
1907
|
-
|
|
2362
|
+
if (content === lastContentRef.current) return;
|
|
2363
|
+
lastContentRef.current = content;
|
|
2364
|
+
const incoming = content?.[A2UI_OPERATIONS_KEY];
|
|
2365
|
+
if (!content || !Array.isArray(incoming)) {
|
|
1908
2366
|
setOperations([]);
|
|
1909
2367
|
return;
|
|
1910
2368
|
}
|
|
1911
|
-
const incoming = content.operations;
|
|
1912
|
-
const signature = stringifyOperations(incoming);
|
|
1913
|
-
if (signature && signature === lastSignatureRef.current) return;
|
|
1914
|
-
lastSignatureRef.current = signature;
|
|
1915
2369
|
setOperations(incoming);
|
|
1916
2370
|
}, [content]);
|
|
1917
2371
|
const groupedOperations = useMemo(() => {
|
|
@@ -1923,7 +2377,7 @@ function createA2UIMessageRenderer(options) {
|
|
|
1923
2377
|
}
|
|
1924
2378
|
return groups;
|
|
1925
2379
|
}, [operations]);
|
|
1926
|
-
if (!groupedOperations.size) return
|
|
2380
|
+
if (!groupedOperations.size) return /* @__PURE__ */ jsx(loadingComponent ?? DefaultA2UILoading, {});
|
|
1927
2381
|
return /* @__PURE__ */ jsx("div", {
|
|
1928
2382
|
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
1929
2383
|
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ jsx(ReactSurfaceHost, {
|
|
@@ -1931,7 +2385,8 @@ function createA2UIMessageRenderer(options) {
|
|
|
1931
2385
|
operations: ops,
|
|
1932
2386
|
theme,
|
|
1933
2387
|
agent,
|
|
1934
|
-
copilotkit
|
|
2388
|
+
copilotkit,
|
|
2389
|
+
catalog
|
|
1935
2390
|
}, surfaceId))
|
|
1936
2391
|
});
|
|
1937
2392
|
}
|
|
@@ -1941,14 +2396,14 @@ function createA2UIMessageRenderer(options) {
|
|
|
1941
2396
|
* Renders a single A2UI surface using the React renderer.
|
|
1942
2397
|
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
1943
2398
|
*/
|
|
1944
|
-
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit }) {
|
|
2399
|
+
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog }) {
|
|
1945
2400
|
return /* @__PURE__ */ jsx("div", {
|
|
1946
2401
|
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
1947
2402
|
children: /* @__PURE__ */ jsxs(A2UIProvider, {
|
|
1948
2403
|
onAction: useCallback(async (message) => {
|
|
1949
2404
|
if (!agent) return;
|
|
2405
|
+
message.userAction;
|
|
1950
2406
|
try {
|
|
1951
|
-
console.info("[A2UI] Action dispatched", message.userAction);
|
|
1952
2407
|
copilotkit.setProperties({
|
|
1953
2408
|
...copilotkit.properties ?? {},
|
|
1954
2409
|
a2uiAction: message
|
|
@@ -1962,47 +2417,501 @@ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit }) {
|
|
|
1962
2417
|
}
|
|
1963
2418
|
}, [agent, copilotkit]),
|
|
1964
2419
|
theme,
|
|
2420
|
+
catalog,
|
|
1965
2421
|
children: [/* @__PURE__ */ jsx(SurfaceMessageProcessor, {
|
|
1966
2422
|
surfaceId,
|
|
1967
2423
|
operations
|
|
1968
|
-
}), /* @__PURE__ */ jsx(
|
|
1969
|
-
surfaceId,
|
|
1970
|
-
className: "cpk:flex cpk:flex-1"
|
|
1971
|
-
})]
|
|
2424
|
+
}), /* @__PURE__ */ jsx(A2UISurfaceOrError, { surfaceId })]
|
|
1972
2425
|
})
|
|
1973
2426
|
});
|
|
1974
2427
|
}
|
|
1975
2428
|
/**
|
|
2429
|
+
* Renders the A2UI surface, or an error message if processing failed.
|
|
2430
|
+
* Must be a child of A2UIProvider to access the error state.
|
|
2431
|
+
*/
|
|
2432
|
+
function A2UISurfaceOrError({ surfaceId }) {
|
|
2433
|
+
const error = useA2UIError();
|
|
2434
|
+
if (error) return /* @__PURE__ */ jsxs("div", {
|
|
2435
|
+
className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
|
|
2436
|
+
children: ["A2UI render error: ", error]
|
|
2437
|
+
});
|
|
2438
|
+
return /* @__PURE__ */ jsx(A2UIRenderer, {
|
|
2439
|
+
surfaceId,
|
|
2440
|
+
className: "cpk:flex cpk:flex-1"
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
1976
2444
|
* Processes A2UI operations into the provider's message processor.
|
|
1977
2445
|
* Must be a child of A2UIProvider to access the actions context.
|
|
1978
2446
|
*/
|
|
1979
2447
|
function SurfaceMessageProcessor({ surfaceId, operations }) {
|
|
1980
|
-
const { processMessages } = useA2UIActions();
|
|
1981
|
-
const
|
|
2448
|
+
const { processMessages, getSurface } = useA2UIActions();
|
|
2449
|
+
const lastHashRef = useRef("");
|
|
1982
2450
|
useEffect(() => {
|
|
1983
|
-
const
|
|
1984
|
-
if (
|
|
1985
|
-
|
|
1986
|
-
processMessages(operations);
|
|
2451
|
+
const hash = JSON.stringify(operations);
|
|
2452
|
+
if (hash === lastHashRef.current) return;
|
|
2453
|
+
lastHashRef.current = hash;
|
|
2454
|
+
processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
|
|
1987
2455
|
}, [
|
|
1988
2456
|
processMessages,
|
|
2457
|
+
getSurface,
|
|
1989
2458
|
surfaceId,
|
|
1990
2459
|
operations
|
|
1991
2460
|
]);
|
|
1992
2461
|
return null;
|
|
1993
2462
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2463
|
+
/**
|
|
2464
|
+
* Default loading component shown while an A2UI surface is generating.
|
|
2465
|
+
* Displays an animated shimmer skeleton.
|
|
2466
|
+
*/
|
|
2467
|
+
function DefaultA2UILoading() {
|
|
2468
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2469
|
+
className: "cpk:flex cpk:flex-col cpk:gap-3 cpk:rounded-xl cpk:border cpk:border-gray-100 cpk:bg-gray-50/50 cpk:p-5",
|
|
2470
|
+
style: { minHeight: 120 },
|
|
2471
|
+
children: [
|
|
2472
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2473
|
+
className: "cpk:flex cpk:items-center cpk:gap-2",
|
|
2474
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2475
|
+
className: "cpk:h-3 cpk:w-3 cpk:rounded-full cpk:bg-gray-200",
|
|
2476
|
+
style: { animation: "cpk-a2ui-pulse 1.5s ease-in-out infinite" }
|
|
2477
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2478
|
+
className: "cpk:text-xs cpk:font-medium cpk:text-gray-400",
|
|
2479
|
+
children: "Generating UI..."
|
|
2480
|
+
})]
|
|
2481
|
+
}),
|
|
2482
|
+
/* @__PURE__ */ jsx("div", {
|
|
2483
|
+
className: "cpk:flex cpk:flex-col cpk:gap-2",
|
|
2484
|
+
children: [
|
|
2485
|
+
.8,
|
|
2486
|
+
.6,
|
|
2487
|
+
.4
|
|
2488
|
+
].map((width, i) => /* @__PURE__ */ jsx("div", {
|
|
2489
|
+
className: "cpk:h-3 cpk:rounded cpk:bg-gray-200/70",
|
|
2490
|
+
style: {
|
|
2491
|
+
width: `${width * 100}%`,
|
|
2492
|
+
animation: `cpk-a2ui-pulse 1.5s ease-in-out ${i * .15}s infinite`
|
|
2493
|
+
}
|
|
2494
|
+
}, i))
|
|
2495
|
+
}),
|
|
2496
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
2497
|
+
@keyframes cpk-a2ui-pulse {
|
|
2498
|
+
0%, 100% { opacity: 0.4; }
|
|
2499
|
+
50% { opacity: 1; }
|
|
2500
|
+
}
|
|
2501
|
+
` })
|
|
2502
|
+
]
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
function getOperationSurfaceId(operation) {
|
|
2506
|
+
if (!operation || typeof operation !== "object") return null;
|
|
2507
|
+
if (typeof operation.surfaceId === "string") return operation.surfaceId;
|
|
2508
|
+
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
//#endregion
|
|
2512
|
+
//#region src/v2/types/defineToolCallRenderer.ts
|
|
2513
|
+
function defineToolCallRenderer(def) {
|
|
2514
|
+
const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
|
|
2515
|
+
return {
|
|
2516
|
+
name: def.name,
|
|
2517
|
+
args: argsSchema,
|
|
2518
|
+
render: def.render,
|
|
2519
|
+
...def.agentId ? { agentId: def.agentId } : {}
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
//#endregion
|
|
2524
|
+
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
2525
|
+
/**
|
|
2526
|
+
* Tool name used by the dynamic A2UI generation secondary LLM.
|
|
2527
|
+
* This renderer is auto-registered when A2UI is enabled.
|
|
2528
|
+
*/
|
|
2529
|
+
const RENDER_A2UI_TOOL_NAME = "render_a2ui";
|
|
2530
|
+
/**
|
|
2531
|
+
* Built-in progress indicator for dynamic A2UI generation.
|
|
2532
|
+
* Shows a skeleton wireframe that progressively reveals as tokens stream in.
|
|
2533
|
+
*
|
|
2534
|
+
* Registered automatically when A2UI is enabled. Users can override by
|
|
2535
|
+
* providing their own `useRenderTool({ name: "render_a2ui", ... })`.
|
|
2536
|
+
*/
|
|
2537
|
+
function A2UIProgressIndicator({ parameters }) {
|
|
2538
|
+
const lastRef = useRef({
|
|
2539
|
+
time: 0,
|
|
2540
|
+
tokens: 0
|
|
2541
|
+
});
|
|
2542
|
+
const now = Date.now();
|
|
2543
|
+
let { tokens } = lastRef.current;
|
|
2544
|
+
if (now - lastRef.current.time > 200) {
|
|
2545
|
+
const chars = JSON.stringify(parameters ?? {}).length;
|
|
2546
|
+
tokens = Math.round(chars / 4);
|
|
2547
|
+
lastRef.current = {
|
|
2548
|
+
time: now,
|
|
2549
|
+
tokens
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
const phase = tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
|
|
2553
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2554
|
+
style: {
|
|
2555
|
+
margin: "12px 0",
|
|
2556
|
+
maxWidth: 320
|
|
2557
|
+
},
|
|
2558
|
+
children: [
|
|
2559
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2560
|
+
style: {
|
|
2561
|
+
position: "relative",
|
|
2562
|
+
overflow: "hidden",
|
|
2563
|
+
borderRadius: 12,
|
|
2564
|
+
border: "1px solid rgba(228,228,231,0.8)",
|
|
2565
|
+
backgroundColor: "#fff",
|
|
2566
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
2567
|
+
padding: "16px 18px 14px"
|
|
2568
|
+
},
|
|
2569
|
+
children: [
|
|
2570
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2571
|
+
style: {
|
|
2572
|
+
display: "flex",
|
|
2573
|
+
alignItems: "center",
|
|
2574
|
+
gap: 8,
|
|
2575
|
+
marginBottom: 12
|
|
2576
|
+
},
|
|
2577
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2578
|
+
style: {
|
|
2579
|
+
display: "flex",
|
|
2580
|
+
gap: 4
|
|
2581
|
+
},
|
|
2582
|
+
children: [
|
|
2583
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2584
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2585
|
+
/* @__PURE__ */ jsx(Dot, {})
|
|
2586
|
+
]
|
|
2587
|
+
}), /* @__PURE__ */ jsx(Bar, {
|
|
2588
|
+
w: 64,
|
|
2589
|
+
h: 6,
|
|
2590
|
+
bg: "#e4e4e7",
|
|
2591
|
+
opacity: phase >= 1 ? 1 : .4,
|
|
2592
|
+
transition: "opacity 0.5s"
|
|
2593
|
+
})]
|
|
2594
|
+
}),
|
|
2595
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2596
|
+
style: {
|
|
2597
|
+
display: "grid",
|
|
2598
|
+
gap: 7
|
|
2599
|
+
},
|
|
2600
|
+
children: [
|
|
2601
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2602
|
+
show: phase >= 0,
|
|
2603
|
+
children: [/* @__PURE__ */ jsx(Bar, {
|
|
2604
|
+
w: 36,
|
|
2605
|
+
h: 7,
|
|
2606
|
+
bg: "rgba(147,197,253,0.7)",
|
|
2607
|
+
anim: 0
|
|
2608
|
+
}), /* @__PURE__ */ jsx(Bar, {
|
|
2609
|
+
w: 80,
|
|
2610
|
+
h: 7,
|
|
2611
|
+
bg: "rgba(219,234,254,0.8)",
|
|
2612
|
+
anim: .2
|
|
2613
|
+
})]
|
|
2614
|
+
}),
|
|
2615
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2616
|
+
show: phase >= 0,
|
|
2617
|
+
delay: .1,
|
|
2618
|
+
children: [
|
|
2619
|
+
/* @__PURE__ */ jsx(Spacer, {}),
|
|
2620
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2621
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2622
|
+
w: 100,
|
|
2623
|
+
h: 7,
|
|
2624
|
+
bg: "rgba(24,24,27,0.2)",
|
|
2625
|
+
anim: .3
|
|
2626
|
+
})
|
|
2627
|
+
]
|
|
2628
|
+
}),
|
|
2629
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2630
|
+
show: phase >= 1,
|
|
2631
|
+
delay: .15,
|
|
2632
|
+
children: [
|
|
2633
|
+
/* @__PURE__ */ jsx(Spacer, {}),
|
|
2634
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2635
|
+
w: 48,
|
|
2636
|
+
h: 7,
|
|
2637
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2638
|
+
anim: .1
|
|
2639
|
+
}),
|
|
2640
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2641
|
+
w: 40,
|
|
2642
|
+
h: 7,
|
|
2643
|
+
bg: "rgba(153,246,228,0.6)",
|
|
2644
|
+
anim: .5
|
|
2645
|
+
}),
|
|
2646
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2647
|
+
w: 56,
|
|
2648
|
+
h: 7,
|
|
2649
|
+
bg: "rgba(147,197,253,0.6)",
|
|
2650
|
+
anim: .3
|
|
2651
|
+
})
|
|
2652
|
+
]
|
|
2653
|
+
}),
|
|
2654
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2655
|
+
show: phase >= 1,
|
|
2656
|
+
delay: .2,
|
|
2657
|
+
children: [
|
|
2658
|
+
/* @__PURE__ */ jsx(Spacer, {}),
|
|
2659
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2660
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2661
|
+
w: 60,
|
|
2662
|
+
h: 7,
|
|
2663
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2664
|
+
anim: .4
|
|
2665
|
+
})
|
|
2666
|
+
]
|
|
2667
|
+
}),
|
|
2668
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2669
|
+
show: phase >= 2,
|
|
2670
|
+
delay: .25,
|
|
2671
|
+
children: [
|
|
2672
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2673
|
+
w: 40,
|
|
2674
|
+
h: 7,
|
|
2675
|
+
bg: "rgba(153,246,228,0.5)",
|
|
2676
|
+
anim: .2
|
|
2677
|
+
}),
|
|
2678
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2679
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2680
|
+
w: 48,
|
|
2681
|
+
h: 7,
|
|
2682
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2683
|
+
anim: .6
|
|
2684
|
+
}),
|
|
2685
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2686
|
+
w: 64,
|
|
2687
|
+
h: 7,
|
|
2688
|
+
bg: "rgba(147,197,253,0.5)",
|
|
2689
|
+
anim: .1
|
|
2690
|
+
})
|
|
2691
|
+
]
|
|
2692
|
+
}),
|
|
2693
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2694
|
+
show: phase >= 2,
|
|
2695
|
+
delay: .3,
|
|
2696
|
+
children: [/* @__PURE__ */ jsx(Bar, {
|
|
2697
|
+
w: 36,
|
|
2698
|
+
h: 7,
|
|
2699
|
+
bg: "rgba(147,197,253,0.6)",
|
|
2700
|
+
anim: .5
|
|
2701
|
+
}), /* @__PURE__ */ jsx(Bar, {
|
|
2702
|
+
w: 36,
|
|
2703
|
+
h: 7,
|
|
2704
|
+
bg: "rgba(24,24,27,0.12)",
|
|
2705
|
+
anim: .7
|
|
2706
|
+
})]
|
|
2707
|
+
}),
|
|
2708
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2709
|
+
show: phase >= 3,
|
|
2710
|
+
delay: .35,
|
|
2711
|
+
children: [
|
|
2712
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2713
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2714
|
+
w: 44,
|
|
2715
|
+
h: 7,
|
|
2716
|
+
bg: "rgba(24,24,27,0.18)",
|
|
2717
|
+
anim: .3
|
|
2718
|
+
}),
|
|
2719
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2720
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2721
|
+
w: 56,
|
|
2722
|
+
h: 7,
|
|
2723
|
+
bg: "rgba(153,246,228,0.5)",
|
|
2724
|
+
anim: .8
|
|
2725
|
+
}),
|
|
2726
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2727
|
+
w: 48,
|
|
2728
|
+
h: 7,
|
|
2729
|
+
bg: "rgba(147,197,253,0.5)",
|
|
2730
|
+
anim: .4
|
|
2731
|
+
})
|
|
2732
|
+
]
|
|
2733
|
+
})
|
|
2734
|
+
]
|
|
2735
|
+
}),
|
|
2736
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
2737
|
+
pointerEvents: "none",
|
|
2738
|
+
position: "absolute",
|
|
2739
|
+
inset: 0,
|
|
2740
|
+
background: "linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
|
|
2741
|
+
backgroundSize: "250% 100%",
|
|
2742
|
+
animation: "cpk-a2ui-sweep 3s ease-in-out infinite"
|
|
2743
|
+
} })
|
|
2744
|
+
]
|
|
2745
|
+
}),
|
|
2746
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2747
|
+
style: {
|
|
2748
|
+
display: "flex",
|
|
2749
|
+
alignItems: "center",
|
|
2750
|
+
justifyContent: "center",
|
|
2751
|
+
gap: 8,
|
|
2752
|
+
marginTop: 8
|
|
2753
|
+
},
|
|
2754
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2755
|
+
style: {
|
|
2756
|
+
fontSize: 12,
|
|
2757
|
+
color: "#a1a1aa",
|
|
2758
|
+
letterSpacing: "0.025em"
|
|
2759
|
+
},
|
|
2760
|
+
children: "Building interface"
|
|
2761
|
+
}), tokens > 0 && /* @__PURE__ */ jsxs("span", {
|
|
2762
|
+
style: {
|
|
2763
|
+
fontSize: 11,
|
|
2764
|
+
color: "#d4d4d8",
|
|
2765
|
+
fontVariantNumeric: "tabular-nums"
|
|
2766
|
+
},
|
|
2767
|
+
children: [
|
|
2768
|
+
"~",
|
|
2769
|
+
tokens.toLocaleString(),
|
|
2770
|
+
" tokens"
|
|
2771
|
+
]
|
|
2772
|
+
})]
|
|
2773
|
+
}),
|
|
2774
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
2775
|
+
@keyframes cpk-a2ui-fade {
|
|
2776
|
+
0%, 100% { opacity: 1; }
|
|
2777
|
+
50% { opacity: 0.5; }
|
|
2778
|
+
}
|
|
2779
|
+
@keyframes cpk-a2ui-sweep {
|
|
2780
|
+
0% { background-position: 250% 0; }
|
|
2781
|
+
100% { background-position: -250% 0; }
|
|
2782
|
+
}
|
|
2783
|
+
` })
|
|
2784
|
+
]
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
function Dot() {
|
|
2788
|
+
return /* @__PURE__ */ jsx("div", { style: {
|
|
2789
|
+
width: 7,
|
|
2790
|
+
height: 7,
|
|
2791
|
+
borderRadius: "50%",
|
|
2792
|
+
backgroundColor: "#d4d4d8",
|
|
2793
|
+
flexShrink: 0
|
|
2794
|
+
} });
|
|
2795
|
+
}
|
|
2796
|
+
function Spacer() {
|
|
2797
|
+
return /* @__PURE__ */ jsx("div", { style: { width: 12 } });
|
|
2798
|
+
}
|
|
2799
|
+
function Bar({ w, h, bg, anim, opacity, transition }) {
|
|
2800
|
+
return /* @__PURE__ */ jsx("div", { style: {
|
|
2801
|
+
width: w,
|
|
2802
|
+
height: h,
|
|
2803
|
+
borderRadius: 9999,
|
|
2804
|
+
backgroundColor: bg,
|
|
2805
|
+
...anim !== void 0 ? { animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite` } : {},
|
|
2806
|
+
...opacity !== void 0 ? { opacity } : {},
|
|
2807
|
+
...transition ? { transition } : {}
|
|
2808
|
+
} });
|
|
2809
|
+
}
|
|
2810
|
+
function Row({ children, show, delay = 0 }) {
|
|
2811
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2812
|
+
style: {
|
|
2813
|
+
display: "flex",
|
|
2814
|
+
alignItems: "center",
|
|
2815
|
+
gap: 6,
|
|
2816
|
+
opacity: show ? 1 : 0,
|
|
2817
|
+
transition: `opacity 0.4s ${delay}s`
|
|
2818
|
+
},
|
|
2819
|
+
children
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
/**
|
|
2823
|
+
* Registers the built-in `render_a2ui` tool call renderer via the props-based
|
|
2824
|
+
* `setRenderToolCalls` mechanism (not `useRenderTool`).
|
|
2825
|
+
*
|
|
2826
|
+
* This ensures user-registered `useRenderTool({ name: "render_a2ui", ... })`
|
|
2827
|
+
* hooks automatically override the built-in, since the merge logic in
|
|
2828
|
+
* react-core.ts gives hook-based entries priority over prop-based entries.
|
|
2829
|
+
*/
|
|
2830
|
+
function A2UIBuiltInToolCallRenderer() {
|
|
2831
|
+
const { copilotkit } = useCopilotKit();
|
|
2832
|
+
useEffect(() => {
|
|
2833
|
+
const renderer = defineToolCallRenderer({
|
|
2834
|
+
name: RENDER_A2UI_TOOL_NAME,
|
|
2835
|
+
args: z.any(),
|
|
2836
|
+
render: ({ status, args: parameters }) => {
|
|
2837
|
+
if (status === "complete") return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2838
|
+
const params = parameters;
|
|
2839
|
+
const items = params?.items;
|
|
2840
|
+
if (Array.isArray(items) && items.length > 0) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2841
|
+
const components = params?.components;
|
|
2842
|
+
if (Array.isArray(components) && components.length > 2) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2843
|
+
return /* @__PURE__ */ jsx(A2UIProgressIndicator, { parameters });
|
|
2844
|
+
}
|
|
2845
|
+
});
|
|
2846
|
+
const existing = copilotkit._renderToolCalls ?? [];
|
|
2847
|
+
copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
|
|
2848
|
+
}, [copilotkit]);
|
|
2849
|
+
return null;
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
//#endregion
|
|
2853
|
+
//#region src/v2/hooks/use-agent-context.tsx
|
|
2854
|
+
function useAgentContext(context) {
|
|
2855
|
+
const { description, value } = context;
|
|
2856
|
+
const { copilotkit } = useCopilotKit();
|
|
2857
|
+
const stringValue = useMemo(() => {
|
|
2858
|
+
if (typeof value === "string") return value;
|
|
2859
|
+
return JSON.stringify(value);
|
|
2860
|
+
}, [value]);
|
|
2861
|
+
useLayoutEffect(() => {
|
|
2862
|
+
if (!copilotkit) return;
|
|
2863
|
+
const id = copilotkit.addContext({
|
|
2864
|
+
description,
|
|
2865
|
+
value: stringValue
|
|
2866
|
+
});
|
|
2867
|
+
return () => {
|
|
2868
|
+
copilotkit.removeContext(id);
|
|
2869
|
+
};
|
|
2870
|
+
}, [
|
|
2871
|
+
description,
|
|
2872
|
+
stringValue,
|
|
2873
|
+
copilotkit
|
|
2874
|
+
]);
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
//#endregion
|
|
2878
|
+
//#region src/v2/a2ui/A2UICatalogContext.tsx
|
|
2879
|
+
/**
|
|
2880
|
+
* Renders agent context describing the available A2UI catalog and custom components.
|
|
2881
|
+
* Only mount this component when A2UI is enabled.
|
|
2882
|
+
*
|
|
2883
|
+
* When `includeSchema` is true, the full component schemas (JSON Schema) are also
|
|
2884
|
+
* sent as context using the same description key as the A2UI middleware, so the
|
|
2885
|
+
* middleware can optionally overwrite it with a server-side schema.
|
|
2886
|
+
*/
|
|
2887
|
+
function A2UICatalogContext({ catalog, includeSchema }) {
|
|
2888
|
+
useAgentContext({
|
|
2889
|
+
description: "A2UI catalog capabilities: available catalog IDs and custom component definitions the client can render.",
|
|
2890
|
+
value: buildCatalogContextValue(catalog)
|
|
2891
|
+
});
|
|
2892
|
+
const { copilotkit } = useCopilotKit();
|
|
2893
|
+
const schemaValue = useMemo(() => includeSchema !== false ? JSON.stringify(extractCatalogComponentSchemas(catalog)) : null, [catalog, includeSchema]);
|
|
2894
|
+
useLayoutEffect(() => {
|
|
2895
|
+
if (!copilotkit || !schemaValue) return;
|
|
2896
|
+
const ids = [];
|
|
2897
|
+
ids.push(copilotkit.addContext({
|
|
2898
|
+
description: A2UI_SCHEMA_CONTEXT_DESCRIPTION,
|
|
2899
|
+
value: schemaValue
|
|
2900
|
+
}));
|
|
2901
|
+
ids.push(copilotkit.addContext({
|
|
2902
|
+
description: "A2UI generation guidelines — protocol rules, tool arguments, path rules, data model format, and form/two-way-binding instructions.",
|
|
2903
|
+
value: A2UI_DEFAULT_GENERATION_GUIDELINES
|
|
2904
|
+
}));
|
|
2905
|
+
ids.push(copilotkit.addContext({
|
|
2906
|
+
description: "A2UI design guidelines — visual design rules, component hierarchy tips, and action handler patterns.",
|
|
2907
|
+
value: A2UI_DEFAULT_DESIGN_GUIDELINES
|
|
2908
|
+
}));
|
|
2909
|
+
return () => {
|
|
2910
|
+
for (const id of ids) copilotkit.removeContext(id);
|
|
2911
|
+
};
|
|
2912
|
+
}, [copilotkit, schemaValue]);
|
|
2913
|
+
return null;
|
|
2914
|
+
}
|
|
2006
2915
|
|
|
2007
2916
|
//#endregion
|
|
2008
2917
|
//#region src/v2/lib/react-core.ts
|
|
@@ -2104,6 +3013,17 @@ var CopilotKitCoreReact = class extends CopilotKitCore {
|
|
|
2104
3013
|
//#region src/v2/providers/CopilotKitProvider.tsx
|
|
2105
3014
|
const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
|
|
2106
3015
|
const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
|
|
3016
|
+
const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
|
|
3017
|
+
|
|
3018
|
+
- Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
|
|
3019
|
+
- Neutral base palette: white backgrounds, zinc/slate gray text (#09090b for headings, #71717a for secondary text). One accent color for interactive elements.
|
|
3020
|
+
- Use system font stacks (system-ui, -apple-system, sans-serif) at readable sizes (14px body, 600 weight for headings). Tight line-heights.
|
|
3021
|
+
- Small, consistent border-radius (6–8px). Cards and containers use border, not shadow, for separation.
|
|
3022
|
+
- Buttons: solid fill for primary (dark bg, white text), outline for secondary (border + transparent bg). Subtle hover state (slight opacity or background shift).
|
|
3023
|
+
- Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
|
|
3024
|
+
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
|
|
3025
|
+
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
|
|
3026
|
+
const GENERATE_SANDBOXED_UI_DESCRIPTION = "Generate sandboxed UI. IMPORTANT: The generated code runs in a sandboxed iframe WITHOUT same-origin access. Do NOT use localStorage, sessionStorage, document.cookie, IndexedDB, or fetch/XMLHttpRequest to same-origin URLs. To communicate with the host application, use Websandbox.connection.remote.<functionName>(args) which returns a Promise.\n\nYou CAN use external libraries from CDNs by including <script> or <link> tags in the HTML <head> (e.g., Chart.js, D3, Three.js, x-data-spreadsheet, etc.). CDN resources load normally inside the sandbox.\n\nPARAMETER ORDER IS CRITICAL — generate parameters in exactly this order:\n1. initialHeight + placeholderMessages (shown to user while generating)\n2. css (all styles FIRST — the user sees a placeholder until CSS is complete)\n3. html (streams in live — the user watches the UI build as HTML is generated)\n4. jsFunctions (reusable helper functions)\n5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
|
|
2107
3027
|
const CopilotKitContext = createContext({
|
|
2108
3028
|
copilotkit: null,
|
|
2109
3029
|
executingToolCallIds: /* @__PURE__ */ new Set()
|
|
@@ -2119,9 +3039,11 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
|
|
|
2119
3039
|
}, [value, warningMessage]);
|
|
2120
3040
|
return value;
|
|
2121
3041
|
}
|
|
2122
|
-
const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, showDevConsole = false, useSingleEndpoint
|
|
3042
|
+
const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor }) => {
|
|
2123
3043
|
const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
|
|
2124
3044
|
const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = useState(false);
|
|
3045
|
+
const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = useState(false);
|
|
3046
|
+
const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
|
|
2125
3047
|
const [runtimeLicenseStatus, setRuntimeLicenseStatus] = useState(void 0);
|
|
2126
3048
|
useEffect(() => {
|
|
2127
3049
|
if (typeof window === "undefined") return;
|
|
@@ -2147,9 +3069,22 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2147
3069
|
content: MCPAppsActivityContentSchema,
|
|
2148
3070
|
render: MCPAppsActivityRenderer
|
|
2149
3071
|
}];
|
|
2150
|
-
if (
|
|
3072
|
+
if (openGenUIActive) renderers.push({
|
|
3073
|
+
activityType: OpenGenerativeUIActivityType,
|
|
3074
|
+
content: OpenGenerativeUIContentSchema,
|
|
3075
|
+
render: OpenGenerativeUIActivityRenderer
|
|
3076
|
+
});
|
|
3077
|
+
if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
|
|
3078
|
+
theme: a2ui?.theme ?? viewerTheme,
|
|
3079
|
+
catalog: a2ui?.catalog,
|
|
3080
|
+
loadingComponent: a2ui?.loadingComponent
|
|
3081
|
+
}));
|
|
2151
3082
|
return renderers;
|
|
2152
|
-
}, [
|
|
3083
|
+
}, [
|
|
3084
|
+
runtimeA2UIEnabled,
|
|
3085
|
+
openGenUIActive,
|
|
3086
|
+
a2ui
|
|
3087
|
+
]);
|
|
2153
3088
|
const allActivityRenderers = useMemo(() => {
|
|
2154
3089
|
return [...renderActivityMessagesList, ...builtInActivityRenderers];
|
|
2155
3090
|
}, [renderActivityMessagesList, builtInActivityRenderers]);
|
|
@@ -2175,6 +3110,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2175
3110
|
const chatApiEndpoint = runtimeUrl ?? (resolvedPublicKey ? COPILOT_CLOUD_CHAT_URL$1 : void 0);
|
|
2176
3111
|
const frontendToolsList = useStableArrayProp(frontendTools, "frontendTools must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.");
|
|
2177
3112
|
const humanInTheLoopList = useStableArrayProp(humanInTheLoop, "humanInTheLoop must be a stable array. If you want to dynamically add or remove human-in-the-loop tools, use `useHumanInTheLoop` instead.");
|
|
3113
|
+
const sandboxFunctionsList = useStableArrayProp(openGenerativeUI?.sandboxFunctions, "openGenerativeUI.sandboxFunctions must be a stable array.");
|
|
2178
3114
|
const processedHumanInTheLoopTools = useMemo(() => {
|
|
2179
3115
|
const processedTools = [];
|
|
2180
3116
|
const processedRenderToolCalls = [];
|
|
@@ -2205,15 +3141,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2205
3141
|
renderToolCalls: processedRenderToolCalls
|
|
2206
3142
|
};
|
|
2207
3143
|
}, [humanInTheLoopList]);
|
|
3144
|
+
const builtInFrontendTools = useMemo(() => {
|
|
3145
|
+
if (!openGenUIActive) return [];
|
|
3146
|
+
return [{
|
|
3147
|
+
name: "generateSandboxedUi",
|
|
3148
|
+
description: GENERATE_SANDBOXED_UI_DESCRIPTION,
|
|
3149
|
+
parameters: GenerateSandboxedUiArgsSchema,
|
|
3150
|
+
handler: async () => "UI generated",
|
|
3151
|
+
followUp: true,
|
|
3152
|
+
render: OpenGenerativeUIToolRenderer
|
|
3153
|
+
}];
|
|
3154
|
+
}, [openGenUIActive]);
|
|
2208
3155
|
const allTools = useMemo(() => {
|
|
2209
3156
|
const tools = [];
|
|
2210
3157
|
tools.push(...frontendToolsList);
|
|
3158
|
+
tools.push(...builtInFrontendTools);
|
|
2211
3159
|
tools.push(...processedHumanInTheLoopTools.tools);
|
|
2212
3160
|
return tools;
|
|
2213
|
-
}, [
|
|
3161
|
+
}, [
|
|
3162
|
+
frontendToolsList,
|
|
3163
|
+
builtInFrontendTools,
|
|
3164
|
+
processedHumanInTheLoopTools
|
|
3165
|
+
]);
|
|
2214
3166
|
const allRenderToolCalls = useMemo(() => {
|
|
2215
3167
|
const combined = [...renderToolCallsList];
|
|
2216
|
-
frontendToolsList.forEach((tool) => {
|
|
3168
|
+
[...frontendToolsList, ...builtInFrontendTools].forEach((tool) => {
|
|
2217
3169
|
if (tool.render) {
|
|
2218
3170
|
const args = tool.parameters || (tool.name === "*" ? z.any() : void 0);
|
|
2219
3171
|
if (args) combined.push({
|
|
@@ -2228,25 +3180,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2228
3180
|
}, [
|
|
2229
3181
|
renderToolCallsList,
|
|
2230
3182
|
frontendToolsList,
|
|
3183
|
+
builtInFrontendTools,
|
|
2231
3184
|
processedHumanInTheLoopTools
|
|
2232
3185
|
]);
|
|
2233
3186
|
const copilotkitRef = useRef(null);
|
|
2234
|
-
if (copilotkitRef.current === null)
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
3187
|
+
if (copilotkitRef.current === null) {
|
|
3188
|
+
copilotkitRef.current = new CopilotKitCoreReact({
|
|
3189
|
+
runtimeUrl: chatApiEndpoint,
|
|
3190
|
+
runtimeTransport: useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto",
|
|
3191
|
+
headers: mergedHeaders,
|
|
3192
|
+
credentials,
|
|
3193
|
+
properties,
|
|
3194
|
+
agents__unsafe_dev_only: mergedAgents,
|
|
3195
|
+
tools: allTools,
|
|
3196
|
+
renderToolCalls: allRenderToolCalls,
|
|
3197
|
+
renderActivityMessages: allActivityRenderers,
|
|
3198
|
+
renderCustomMessages: renderCustomMessagesList
|
|
3199
|
+
});
|
|
3200
|
+
if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
|
|
3201
|
+
}
|
|
2246
3202
|
const copilotkit = copilotkitRef.current;
|
|
2247
3203
|
useEffect(() => {
|
|
3204
|
+
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
|
|
2248
3205
|
const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
2249
3206
|
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
|
|
3207
|
+
setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
|
|
2250
3208
|
setRuntimeLicenseStatus(copilotkit.licenseStatus);
|
|
2251
3209
|
} });
|
|
2252
3210
|
return () => {
|
|
@@ -2305,7 +3263,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2305
3263
|
}, [copilotkit]);
|
|
2306
3264
|
useEffect(() => {
|
|
2307
3265
|
copilotkit.setRuntimeUrl(chatApiEndpoint);
|
|
2308
|
-
copilotkit.setRuntimeTransport(useSingleEndpoint ? "single" : "rest");
|
|
3266
|
+
copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
|
|
2309
3267
|
copilotkit.setHeaders(mergedHeaders);
|
|
2310
3268
|
copilotkit.setCredentials(credentials);
|
|
2311
3269
|
copilotkit.setProperties(properties);
|
|
@@ -2339,23 +3297,75 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2339
3297
|
useEffect(() => {
|
|
2340
3298
|
didMountRef.current = true;
|
|
2341
3299
|
}, []);
|
|
3300
|
+
useEffect(() => {
|
|
3301
|
+
if (defaultThrottleMs !== void 0 && (!Number.isFinite(defaultThrottleMs) || defaultThrottleMs < 0)) console.error(`CopilotKitProvider: defaultThrottleMs must be a non-negative finite number, got ${defaultThrottleMs}. useAgent hooks without an explicit throttleMs will fall back to unthrottled.`);
|
|
3302
|
+
copilotkit.setDefaultThrottleMs(defaultThrottleMs);
|
|
3303
|
+
}, [copilotkit, defaultThrottleMs]);
|
|
3304
|
+
const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
|
|
3305
|
+
useLayoutEffect(() => {
|
|
3306
|
+
if (!copilotkit || !openGenUIActive) return;
|
|
3307
|
+
const id = copilotkit.addContext({
|
|
3308
|
+
description: "Design guidelines for the generateSandboxedUi tool. Follow these when building UI.",
|
|
3309
|
+
value: designSkill
|
|
3310
|
+
});
|
|
3311
|
+
return () => {
|
|
3312
|
+
copilotkit.removeContext(id);
|
|
3313
|
+
};
|
|
3314
|
+
}, [
|
|
3315
|
+
copilotkit,
|
|
3316
|
+
designSkill,
|
|
3317
|
+
openGenUIActive
|
|
3318
|
+
]);
|
|
3319
|
+
const sandboxFunctionsDescriptors = useMemo(() => {
|
|
3320
|
+
if (sandboxFunctionsList.length === 0) return null;
|
|
3321
|
+
return JSON.stringify(sandboxFunctionsList.map((fn) => ({
|
|
3322
|
+
name: fn.name,
|
|
3323
|
+
description: fn.description,
|
|
3324
|
+
parameters: schemaToJsonSchema(fn.parameters, { zodToJsonSchema })
|
|
3325
|
+
})));
|
|
3326
|
+
}, [sandboxFunctionsList]);
|
|
3327
|
+
useLayoutEffect(() => {
|
|
3328
|
+
if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
|
|
3329
|
+
const id = copilotkit.addContext({
|
|
3330
|
+
description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
|
|
3331
|
+
value: sandboxFunctionsDescriptors
|
|
3332
|
+
});
|
|
3333
|
+
return () => {
|
|
3334
|
+
copilotkit.removeContext(id);
|
|
3335
|
+
};
|
|
3336
|
+
}, [
|
|
3337
|
+
copilotkit,
|
|
3338
|
+
sandboxFunctionsDescriptors,
|
|
3339
|
+
openGenUIActive
|
|
3340
|
+
]);
|
|
2342
3341
|
const contextValue = useMemo(() => ({
|
|
2343
3342
|
copilotkit,
|
|
2344
3343
|
executingToolCallIds
|
|
2345
3344
|
}), [copilotkit, executingToolCallIds]);
|
|
2346
3345
|
const licenseContextValue = useMemo(() => createLicenseContextValue(null), []);
|
|
2347
|
-
return /* @__PURE__ */ jsx(
|
|
2348
|
-
value:
|
|
2349
|
-
children: /* @__PURE__ */
|
|
2350
|
-
value:
|
|
2351
|
-
children:
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
3346
|
+
return /* @__PURE__ */ jsx(SandboxFunctionsContext.Provider, {
|
|
3347
|
+
value: sandboxFunctionsList,
|
|
3348
|
+
children: /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
|
|
3349
|
+
value: contextValue,
|
|
3350
|
+
children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
|
|
3351
|
+
value: licenseContextValue,
|
|
3352
|
+
children: [
|
|
3353
|
+
runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
|
|
3354
|
+
runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UICatalogContext, {
|
|
3355
|
+
catalog: a2ui?.catalog,
|
|
3356
|
+
includeSchema: a2ui?.includeSchema
|
|
3357
|
+
}),
|
|
3358
|
+
children,
|
|
3359
|
+
shouldRenderInspector ? /* @__PURE__ */ jsx(CopilotKitInspector, {
|
|
3360
|
+
core: copilotkit,
|
|
3361
|
+
defaultAnchor: inspectorDefaultAnchor
|
|
3362
|
+
}) : null,
|
|
3363
|
+
runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "no_license" }),
|
|
3364
|
+
runtimeLicenseStatus === "expired" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expired" }),
|
|
3365
|
+
runtimeLicenseStatus === "invalid" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "invalid" }),
|
|
3366
|
+
runtimeLicenseStatus === "expiring" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expiring" })
|
|
3367
|
+
]
|
|
3368
|
+
})
|
|
2359
3369
|
})
|
|
2360
3370
|
});
|
|
2361
3371
|
};
|
|
@@ -2496,11 +3506,21 @@ function getOrCreateThreadClone(existing, threadId, headers) {
|
|
|
2496
3506
|
byThread.set(threadId, clone);
|
|
2497
3507
|
return clone;
|
|
2498
3508
|
}
|
|
2499
|
-
function useAgent({ agentId, threadId, updates } = {}) {
|
|
3509
|
+
function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
|
|
2500
3510
|
agentId ??= DEFAULT_AGENT_ID;
|
|
2501
3511
|
const { copilotkit } = useCopilotKit();
|
|
3512
|
+
const providerThrottleMs = copilotkit.defaultThrottleMs;
|
|
2502
3513
|
const chatConfig = useCopilotChatConfiguration();
|
|
2503
3514
|
threadId ??= chatConfig?.threadId;
|
|
3515
|
+
const effectiveThrottleMs = useMemo(() => {
|
|
3516
|
+
const resolved = throttleMs ?? providerThrottleMs ?? 0;
|
|
3517
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
3518
|
+
const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
|
|
3519
|
+
console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
|
|
3520
|
+
return 0;
|
|
3521
|
+
}
|
|
3522
|
+
return resolved;
|
|
3523
|
+
}, [throttleMs, providerThrottleMs]);
|
|
2504
3524
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
2505
3525
|
const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
|
|
2506
3526
|
const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
|
|
@@ -2564,9 +3584,32 @@ function useAgent({ agentId, threadId, updates } = {}) {
|
|
|
2564
3584
|
useEffect(() => {
|
|
2565
3585
|
if (updateFlags.length === 0) return;
|
|
2566
3586
|
const handlers = {};
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
3587
|
+
let timerId = null;
|
|
3588
|
+
let active = true;
|
|
3589
|
+
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
|
|
3590
|
+
const ms = effectiveThrottleMs;
|
|
3591
|
+
if (ms > 0) {
|
|
3592
|
+
let throttleActive = false;
|
|
3593
|
+
let pending = false;
|
|
3594
|
+
const throttledNotify = () => {
|
|
3595
|
+
if (!active) return;
|
|
3596
|
+
if (!throttleActive) {
|
|
3597
|
+
throttleActive = true;
|
|
3598
|
+
pending = false;
|
|
3599
|
+
forceUpdate();
|
|
3600
|
+
timerId = setTimeout(function trailingEdge() {
|
|
3601
|
+
timerId = null;
|
|
3602
|
+
if (active && pending) {
|
|
3603
|
+
pending = false;
|
|
3604
|
+
forceUpdate();
|
|
3605
|
+
timerId = setTimeout(trailingEdge, ms);
|
|
3606
|
+
} else throttleActive = false;
|
|
3607
|
+
}, ms);
|
|
3608
|
+
} else pending = true;
|
|
3609
|
+
};
|
|
3610
|
+
handlers.onMessagesChanged = throttledNotify;
|
|
3611
|
+
} else handlers.onMessagesChanged = forceUpdate;
|
|
3612
|
+
}
|
|
2570
3613
|
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
|
|
2571
3614
|
if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
|
|
2572
3615
|
handlers.onRunInitialized = forceUpdate;
|
|
@@ -2574,11 +3617,16 @@ function useAgent({ agentId, threadId, updates } = {}) {
|
|
|
2574
3617
|
handlers.onRunFailed = forceUpdate;
|
|
2575
3618
|
}
|
|
2576
3619
|
const subscription = agent.subscribe(handlers);
|
|
2577
|
-
return () =>
|
|
3620
|
+
return () => {
|
|
3621
|
+
active = false;
|
|
3622
|
+
if (timerId !== null) clearTimeout(timerId);
|
|
3623
|
+
subscription.unsubscribe();
|
|
3624
|
+
};
|
|
2578
3625
|
}, [
|
|
2579
3626
|
agent,
|
|
2580
3627
|
forceUpdate,
|
|
2581
|
-
|
|
3628
|
+
effectiveThrottleMs,
|
|
3629
|
+
updateFlags
|
|
2582
3630
|
]);
|
|
2583
3631
|
useEffect(() => {
|
|
2584
3632
|
if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
|
|
@@ -2636,7 +3684,8 @@ function useRenderCustomMessages() {
|
|
|
2636
3684
|
//#region src/v2/hooks/use-render-activity-message.tsx
|
|
2637
3685
|
function useRenderActivityMessage() {
|
|
2638
3686
|
const { copilotkit } = useCopilotKit();
|
|
2639
|
-
const
|
|
3687
|
+
const config = useCopilotChatConfiguration();
|
|
3688
|
+
const agentId = config?.agentId ?? DEFAULT_AGENT_ID;
|
|
2640
3689
|
const renderers = copilotkit.renderActivityMessages;
|
|
2641
3690
|
const findRenderer = useCallback((activityType) => {
|
|
2642
3691
|
if (!renderers.length) return null;
|
|
@@ -2652,7 +3701,8 @@ function useRenderActivityMessage() {
|
|
|
2652
3701
|
return null;
|
|
2653
3702
|
}
|
|
2654
3703
|
const Component = renderer.render;
|
|
2655
|
-
const
|
|
3704
|
+
const registryAgent = copilotkit.getAgent(agentId);
|
|
3705
|
+
const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
|
|
2656
3706
|
return /* @__PURE__ */ jsx(Component, {
|
|
2657
3707
|
activityType: message.activityType,
|
|
2658
3708
|
content: parseResult.data,
|
|
@@ -2661,6 +3711,7 @@ function useRenderActivityMessage() {
|
|
|
2661
3711
|
}, message.id);
|
|
2662
3712
|
}, [
|
|
2663
3713
|
agentId,
|
|
3714
|
+
config?.threadId,
|
|
2664
3715
|
copilotkit,
|
|
2665
3716
|
findRenderer
|
|
2666
3717
|
]);
|
|
@@ -2686,7 +3737,7 @@ function useFrontendTool(tool, deps) {
|
|
|
2686
3737
|
copilotkit.removeTool(name, tool.agentId);
|
|
2687
3738
|
}
|
|
2688
3739
|
copilotkit.addTool(tool);
|
|
2689
|
-
if (tool.render
|
|
3740
|
+
if (tool.render) copilotkit.addHookRenderToolCall({
|
|
2690
3741
|
name,
|
|
2691
3742
|
args: tool.parameters,
|
|
2692
3743
|
agentId: tool.agentId,
|
|
@@ -2771,18 +3822,6 @@ function useComponent(config, deps) {
|
|
|
2771
3822
|
}, deps);
|
|
2772
3823
|
}
|
|
2773
3824
|
|
|
2774
|
-
//#endregion
|
|
2775
|
-
//#region src/v2/types/defineToolCallRenderer.ts
|
|
2776
|
-
function defineToolCallRenderer(def) {
|
|
2777
|
-
const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
|
|
2778
|
-
return {
|
|
2779
|
-
name: def.name,
|
|
2780
|
-
args: argsSchema,
|
|
2781
|
-
render: def.render,
|
|
2782
|
-
...def.agentId ? { agentId: def.agentId } : {}
|
|
2783
|
-
};
|
|
2784
|
-
}
|
|
2785
|
-
|
|
2786
3825
|
//#endregion
|
|
2787
3826
|
//#region src/v2/hooks/use-render-tool.tsx
|
|
2788
3827
|
const EMPTY_DEPS = [];
|
|
@@ -3112,31 +4151,6 @@ function useHumanInTheLoop(tool, deps) {
|
|
|
3112
4151
|
]);
|
|
3113
4152
|
}
|
|
3114
4153
|
|
|
3115
|
-
//#endregion
|
|
3116
|
-
//#region src/v2/hooks/use-agent-context.tsx
|
|
3117
|
-
function useAgentContext(context) {
|
|
3118
|
-
const { description, value } = context;
|
|
3119
|
-
const { copilotkit } = useCopilotKit();
|
|
3120
|
-
const stringValue = useMemo(() => {
|
|
3121
|
-
if (typeof value === "string") return value;
|
|
3122
|
-
return JSON.stringify(value);
|
|
3123
|
-
}, [value]);
|
|
3124
|
-
useLayoutEffect(() => {
|
|
3125
|
-
if (!copilotkit) return;
|
|
3126
|
-
const id = copilotkit.addContext({
|
|
3127
|
-
description,
|
|
3128
|
-
value: stringValue
|
|
3129
|
-
});
|
|
3130
|
-
return () => {
|
|
3131
|
-
copilotkit.removeContext(id);
|
|
3132
|
-
};
|
|
3133
|
-
}, [
|
|
3134
|
-
description,
|
|
3135
|
-
stringValue,
|
|
3136
|
-
copilotkit
|
|
3137
|
-
]);
|
|
3138
|
-
}
|
|
3139
|
-
|
|
3140
4154
|
//#endregion
|
|
3141
4155
|
//#region src/v2/hooks/use-suggestions.tsx
|
|
3142
4156
|
function useSuggestions({ agentId } = {}) {
|
|
@@ -3546,11 +4560,19 @@ function useThreadStoreSelector(store, selector) {
|
|
|
3546
4560
|
function useThreads$1({ agentId, includeArchived, limit }) {
|
|
3547
4561
|
const { copilotkit } = useCopilotKit();
|
|
3548
4562
|
const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
|
|
3549
|
-
const
|
|
4563
|
+
const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
|
|
4564
|
+
const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
|
|
4565
|
+
id,
|
|
4566
|
+
agentId,
|
|
4567
|
+
name,
|
|
4568
|
+
archived,
|
|
4569
|
+
createdAt,
|
|
4570
|
+
updatedAt
|
|
4571
|
+
})), [coreThreads]);
|
|
3550
4572
|
const storeIsLoading = useThreadStoreSelector(store, ɵselectThreadsIsLoading);
|
|
3551
4573
|
const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
|
|
3552
|
-
const
|
|
3553
|
-
const
|
|
4574
|
+
const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
|
|
4575
|
+
const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
|
|
3554
4576
|
const headersKey = useMemo(() => {
|
|
3555
4577
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
3556
4578
|
}, [copilotkit.headers]);
|
|
@@ -3593,15 +4615,173 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
3593
4615
|
threads,
|
|
3594
4616
|
isLoading,
|
|
3595
4617
|
error,
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
4618
|
+
hasMoreThreads,
|
|
4619
|
+
isFetchingMoreThreads,
|
|
4620
|
+
fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
|
|
3599
4621
|
renameThread,
|
|
3600
4622
|
archiveThread,
|
|
3601
4623
|
deleteThread
|
|
3602
4624
|
};
|
|
3603
4625
|
}
|
|
3604
4626
|
|
|
4627
|
+
//#endregion
|
|
4628
|
+
//#region src/v2/hooks/use-attachments.tsx
|
|
4629
|
+
/**
|
|
4630
|
+
* Hook that manages file attachment state — uploads, drag-and-drop, paste,
|
|
4631
|
+
* and lifecycle. All returned callbacks are referentially stable across
|
|
4632
|
+
* renders (via useCallback) to avoid destabilizing downstream memoization.
|
|
4633
|
+
*/
|
|
4634
|
+
function useAttachments({ config }) {
|
|
4635
|
+
const enabled = config?.enabled ?? false;
|
|
4636
|
+
const [attachments, setAttachments] = useState([]);
|
|
4637
|
+
const [dragOver, setDragOver] = useState(false);
|
|
4638
|
+
const fileInputRef = useRef(null);
|
|
4639
|
+
const containerRef = useRef(null);
|
|
4640
|
+
const configRef = useRef(config);
|
|
4641
|
+
configRef.current = config;
|
|
4642
|
+
const attachmentsRef = useRef([]);
|
|
4643
|
+
attachmentsRef.current = attachments;
|
|
4644
|
+
const processFiles = useCallback(async (files) => {
|
|
4645
|
+
const cfg = configRef.current;
|
|
4646
|
+
const accept = cfg?.accept ?? "*/*";
|
|
4647
|
+
const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
|
|
4648
|
+
const rejectedFiles = files.filter((file) => !matchesAcceptFilter(file, accept));
|
|
4649
|
+
for (const file of rejectedFiles) cfg?.onUploadFailed?.({
|
|
4650
|
+
reason: "invalid-type",
|
|
4651
|
+
file,
|
|
4652
|
+
message: `File "${file.name}" is not accepted. Supported types: ${accept}`
|
|
4653
|
+
});
|
|
4654
|
+
const validFiles = files.filter((file) => matchesAcceptFilter(file, accept));
|
|
4655
|
+
for (const file of validFiles) {
|
|
4656
|
+
if (exceedsMaxSize(file, maxSize)) {
|
|
4657
|
+
cfg?.onUploadFailed?.({
|
|
4658
|
+
reason: "file-too-large",
|
|
4659
|
+
file,
|
|
4660
|
+
message: `File "${file.name}" exceeds the maximum size of ${formatFileSize(maxSize)}`
|
|
4661
|
+
});
|
|
4662
|
+
continue;
|
|
4663
|
+
}
|
|
4664
|
+
const modality = getModalityFromMimeType(file.type);
|
|
4665
|
+
const placeholderId = randomUUID();
|
|
4666
|
+
const placeholder = {
|
|
4667
|
+
id: placeholderId,
|
|
4668
|
+
type: modality,
|
|
4669
|
+
source: {
|
|
4670
|
+
type: "data",
|
|
4671
|
+
value: "",
|
|
4672
|
+
mimeType: file.type
|
|
4673
|
+
},
|
|
4674
|
+
filename: file.name,
|
|
4675
|
+
size: file.size,
|
|
4676
|
+
status: "uploading"
|
|
4677
|
+
};
|
|
4678
|
+
setAttachments((prev) => [...prev, placeholder]);
|
|
4679
|
+
try {
|
|
4680
|
+
let source;
|
|
4681
|
+
let uploadMetadata;
|
|
4682
|
+
if (cfg?.onUpload) {
|
|
4683
|
+
const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
|
|
4684
|
+
source = uploadSource;
|
|
4685
|
+
uploadMetadata = meta;
|
|
4686
|
+
} else source = {
|
|
4687
|
+
type: "data",
|
|
4688
|
+
value: await readFileAsBase64(file),
|
|
4689
|
+
mimeType: file.type
|
|
4690
|
+
};
|
|
4691
|
+
let thumbnail;
|
|
4692
|
+
if (modality === "video") thumbnail = await generateVideoThumbnail(file);
|
|
4693
|
+
setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
|
|
4694
|
+
...att,
|
|
4695
|
+
source,
|
|
4696
|
+
status: "ready",
|
|
4697
|
+
thumbnail,
|
|
4698
|
+
metadata: uploadMetadata
|
|
4699
|
+
} : att));
|
|
4700
|
+
} catch (error) {
|
|
4701
|
+
setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
|
|
4702
|
+
console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
|
|
4703
|
+
cfg?.onUploadFailed?.({
|
|
4704
|
+
reason: "upload-failed",
|
|
4705
|
+
file,
|
|
4706
|
+
message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
|
|
4707
|
+
});
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
}, []);
|
|
4711
|
+
const handleFileUpload = useCallback(async (e) => {
|
|
4712
|
+
if (!e.target.files?.length) return;
|
|
4713
|
+
try {
|
|
4714
|
+
await processFiles(Array.from(e.target.files));
|
|
4715
|
+
} catch (error) {
|
|
4716
|
+
console.error("[CopilotKit] Upload error:", error);
|
|
4717
|
+
}
|
|
4718
|
+
}, [processFiles]);
|
|
4719
|
+
const handleDragOver = useCallback((e) => {
|
|
4720
|
+
if (!configRef.current?.enabled) return;
|
|
4721
|
+
e.preventDefault();
|
|
4722
|
+
e.stopPropagation();
|
|
4723
|
+
setDragOver(true);
|
|
4724
|
+
}, []);
|
|
4725
|
+
const handleDragLeave = useCallback((e) => {
|
|
4726
|
+
e.preventDefault();
|
|
4727
|
+
e.stopPropagation();
|
|
4728
|
+
setDragOver(false);
|
|
4729
|
+
}, []);
|
|
4730
|
+
const handleDrop = useCallback(async (e) => {
|
|
4731
|
+
e.preventDefault();
|
|
4732
|
+
e.stopPropagation();
|
|
4733
|
+
setDragOver(false);
|
|
4734
|
+
if (!configRef.current?.enabled) return;
|
|
4735
|
+
const files = Array.from(e.dataTransfer.files);
|
|
4736
|
+
if (files.length > 0) try {
|
|
4737
|
+
await processFiles(files);
|
|
4738
|
+
} catch (error) {
|
|
4739
|
+
console.error("[CopilotKit] Drop error:", error);
|
|
4740
|
+
}
|
|
4741
|
+
}, [processFiles]);
|
|
4742
|
+
useEffect(() => {
|
|
4743
|
+
if (!enabled) return;
|
|
4744
|
+
const handlePaste = async (e) => {
|
|
4745
|
+
const target = e.target;
|
|
4746
|
+
if (!target || !containerRef.current?.contains(target)) return;
|
|
4747
|
+
const accept = configRef.current?.accept ?? "*/*";
|
|
4748
|
+
const fileItems = Array.from(e.clipboardData?.items || []).filter((item) => item.kind === "file" && item.getAsFile() !== null && matchesAcceptFilter(item.getAsFile(), accept));
|
|
4749
|
+
if (fileItems.length === 0) return;
|
|
4750
|
+
e.preventDefault();
|
|
4751
|
+
const files = fileItems.map((item) => item.getAsFile()).filter((f) => f !== null);
|
|
4752
|
+
try {
|
|
4753
|
+
await processFiles(files);
|
|
4754
|
+
} catch (error) {
|
|
4755
|
+
console.error("[CopilotKit] Paste error:", error);
|
|
4756
|
+
}
|
|
4757
|
+
};
|
|
4758
|
+
document.addEventListener("paste", handlePaste);
|
|
4759
|
+
return () => document.removeEventListener("paste", handlePaste);
|
|
4760
|
+
}, [enabled, processFiles]);
|
|
4761
|
+
return {
|
|
4762
|
+
attachments,
|
|
4763
|
+
enabled,
|
|
4764
|
+
dragOver,
|
|
4765
|
+
fileInputRef,
|
|
4766
|
+
containerRef,
|
|
4767
|
+
processFiles,
|
|
4768
|
+
handleFileUpload,
|
|
4769
|
+
handleDragOver,
|
|
4770
|
+
handleDragLeave,
|
|
4771
|
+
handleDrop,
|
|
4772
|
+
removeAttachment: useCallback((id) => {
|
|
4773
|
+
setAttachments((prev) => prev.filter((a) => a.id !== id));
|
|
4774
|
+
}, []),
|
|
4775
|
+
consumeAttachments: useCallback(() => {
|
|
4776
|
+
const ready = attachmentsRef.current.filter((a) => a.status === "ready");
|
|
4777
|
+
if (ready.length === 0) return ready;
|
|
4778
|
+
setAttachments((prev) => prev.filter((a) => a.status !== "ready"));
|
|
4779
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
4780
|
+
return ready;
|
|
4781
|
+
}, [])
|
|
4782
|
+
};
|
|
4783
|
+
}
|
|
4784
|
+
|
|
3605
4785
|
//#endregion
|
|
3606
4786
|
//#region src/v2/components/chat/CopilotChatToolCallsView.tsx
|
|
3607
4787
|
function CopilotChatToolCallsView({ message, messages = [] }) {
|
|
@@ -3718,9 +4898,19 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
|
|
|
3718
4898
|
_CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
|
|
3719
4899
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
3720
4900
|
const [copied, setCopied] = useState(false);
|
|
4901
|
+
const timerRef = useRef(null);
|
|
4902
|
+
useEffect(() => {
|
|
4903
|
+
return () => {
|
|
4904
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
4905
|
+
};
|
|
4906
|
+
}, []);
|
|
3721
4907
|
const handleClick = (event) => {
|
|
3722
4908
|
setCopied(true);
|
|
3723
|
-
|
|
4909
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
4910
|
+
timerRef.current = setTimeout(() => {
|
|
4911
|
+
timerRef.current = null;
|
|
4912
|
+
setCopied(false);
|
|
4913
|
+
}, 2e3);
|
|
3724
4914
|
if (onClick) onClick(event);
|
|
3725
4915
|
};
|
|
3726
4916
|
return /* @__PURE__ */ jsx(ToolbarButton, {
|
|
@@ -3778,6 +4968,79 @@ CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantM
|
|
|
3778
4968
|
CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
|
|
3779
4969
|
var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
|
|
3780
4970
|
|
|
4971
|
+
//#endregion
|
|
4972
|
+
//#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
|
|
4973
|
+
const ImageAttachment = memo(function ImageAttachment({ src, className }) {
|
|
4974
|
+
const [error, setError] = useState(false);
|
|
4975
|
+
if (error) return /* @__PURE__ */ jsx("div", {
|
|
4976
|
+
className: cn("cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:rounded-lg cpk:bg-muted cpk:p-4 cpk:text-sm cpk:text-muted-foreground", className),
|
|
4977
|
+
children: /* @__PURE__ */ jsx("span", { children: "Failed to load image" })
|
|
4978
|
+
});
|
|
4979
|
+
return /* @__PURE__ */ jsx("img", {
|
|
4980
|
+
src,
|
|
4981
|
+
alt: "Image attachment",
|
|
4982
|
+
className: cn("cpk:max-w-full cpk:h-auto cpk:rounded-lg", className),
|
|
4983
|
+
onError: () => setError(true)
|
|
4984
|
+
});
|
|
4985
|
+
});
|
|
4986
|
+
const AudioAttachment = memo(function AudioAttachment({ src, filename, className }) {
|
|
4987
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4988
|
+
className: cn("cpk:flex cpk:flex-col cpk:gap-1", className),
|
|
4989
|
+
children: [/* @__PURE__ */ jsx("audio", {
|
|
4990
|
+
src,
|
|
4991
|
+
controls: true,
|
|
4992
|
+
preload: "metadata",
|
|
4993
|
+
className: "cpk:max-w-[300px] cpk:w-full cpk:h-10"
|
|
4994
|
+
}), filename && /* @__PURE__ */ jsx("span", {
|
|
4995
|
+
className: "cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]",
|
|
4996
|
+
children: filename
|
|
4997
|
+
})]
|
|
4998
|
+
});
|
|
4999
|
+
});
|
|
5000
|
+
const VideoAttachment = memo(function VideoAttachment({ src, className }) {
|
|
5001
|
+
return /* @__PURE__ */ jsx("video", {
|
|
5002
|
+
src,
|
|
5003
|
+
controls: true,
|
|
5004
|
+
preload: "metadata",
|
|
5005
|
+
className: cn("cpk:max-w-[400px] cpk:w-full cpk:rounded-lg", className)
|
|
5006
|
+
});
|
|
5007
|
+
});
|
|
5008
|
+
const DocumentAttachment = memo(function DocumentAttachment({ source, filename, className }) {
|
|
5009
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5010
|
+
className: cn("cpk:inline-flex cpk:items-center cpk:gap-2 cpk:px-3 cpk:py-2 cpk:border cpk:border-border cpk:rounded-lg cpk:bg-muted", className),
|
|
5011
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5012
|
+
className: "cpk:text-xs cpk:font-bold cpk:uppercase",
|
|
5013
|
+
children: getDocumentIcon(source.mimeType ?? "")
|
|
5014
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
5015
|
+
className: "cpk:text-sm cpk:text-muted-foreground cpk:truncate",
|
|
5016
|
+
children: filename || source.mimeType || "Unknown type"
|
|
5017
|
+
})]
|
|
5018
|
+
});
|
|
5019
|
+
});
|
|
5020
|
+
const CopilotChatAttachmentRenderer = ({ type, source, filename, className }) => {
|
|
5021
|
+
const src = getSourceUrl(source);
|
|
5022
|
+
switch (type) {
|
|
5023
|
+
case "image": return /* @__PURE__ */ jsx(ImageAttachment, {
|
|
5024
|
+
src,
|
|
5025
|
+
className
|
|
5026
|
+
});
|
|
5027
|
+
case "audio": return /* @__PURE__ */ jsx(AudioAttachment, {
|
|
5028
|
+
src,
|
|
5029
|
+
filename,
|
|
5030
|
+
className
|
|
5031
|
+
});
|
|
5032
|
+
case "video": return /* @__PURE__ */ jsx(VideoAttachment, {
|
|
5033
|
+
src,
|
|
5034
|
+
className
|
|
5035
|
+
});
|
|
5036
|
+
case "document": return /* @__PURE__ */ jsx(DocumentAttachment, {
|
|
5037
|
+
source,
|
|
5038
|
+
filename,
|
|
5039
|
+
className
|
|
5040
|
+
});
|
|
5041
|
+
}
|
|
5042
|
+
};
|
|
5043
|
+
|
|
3781
5044
|
//#endregion
|
|
3782
5045
|
//#region src/v2/components/chat/CopilotChatUserMessage.tsx
|
|
3783
5046
|
function flattenUserMessageContent(content) {
|
|
@@ -3788,8 +5051,17 @@ function flattenUserMessageContent(content) {
|
|
|
3788
5051
|
return "";
|
|
3789
5052
|
}).filter((text) => text.length > 0).join("\n");
|
|
3790
5053
|
}
|
|
5054
|
+
function getMediaParts(content) {
|
|
5055
|
+
if (!content || typeof content === "string") return [];
|
|
5056
|
+
return content.filter((part) => part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document");
|
|
5057
|
+
}
|
|
5058
|
+
function getFilename(part) {
|
|
5059
|
+
const meta = part.metadata;
|
|
5060
|
+
if (meta != null && typeof meta === "object" && "filename" in meta && typeof meta.filename === "string") return meta.filename;
|
|
5061
|
+
}
|
|
3791
5062
|
function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfBranches, onSwitchToBranch, additionalToolbarItems, messageRenderer, toolbar, copyButton, editButton, branchNavigation, children, className, ...props }) {
|
|
3792
5063
|
const flattenedContent = useMemo(() => flattenUserMessageContent(message.content), [message.content]);
|
|
5064
|
+
const mediaParts = useMemo(() => getMediaParts(message.content), [message.content]);
|
|
3793
5065
|
const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
|
|
3794
5066
|
const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
|
|
3795
5067
|
if (flattenedContent) try {
|
|
@@ -3836,7 +5108,18 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
3836
5108
|
className: twMerge("copilotKitMessage copilotKitUserMessage cpk:flex cpk:flex-col cpk:items-end cpk:group cpk:pt-10", className),
|
|
3837
5109
|
"data-message-id": message.id,
|
|
3838
5110
|
...props,
|
|
3839
|
-
children: [
|
|
5111
|
+
children: [
|
|
5112
|
+
BoundMessageRenderer,
|
|
5113
|
+
mediaParts.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
5114
|
+
className: "cpk:flex cpk:flex-col cpk:items-end cpk:gap-2 cpk:mt-2",
|
|
5115
|
+
children: mediaParts.map((part, index) => /* @__PURE__ */ jsx(CopilotChatAttachmentRenderer, {
|
|
5116
|
+
type: part.type,
|
|
5117
|
+
source: part.source,
|
|
5118
|
+
filename: getFilename(part)
|
|
5119
|
+
}, index))
|
|
5120
|
+
}),
|
|
5121
|
+
BoundToolbar
|
|
5122
|
+
]
|
|
3840
5123
|
});
|
|
3841
5124
|
}
|
|
3842
5125
|
(function(_CopilotChatUserMessage) {
|
|
@@ -4134,6 +5417,7 @@ const CopilotChatSuggestionView = React.forwardRef(function CopilotChatSuggestio
|
|
|
4134
5417
|
const isLoading = loadingSet.has(index) || suggestion.isLoading === true;
|
|
4135
5418
|
const pill = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
|
|
4136
5419
|
children: suggestion.title,
|
|
5420
|
+
className: suggestion.className,
|
|
4137
5421
|
isLoading,
|
|
4138
5422
|
type: "button",
|
|
4139
5423
|
onClick: () => onSelectSuggestion?.(suggestion, index)
|
|
@@ -4170,9 +5454,43 @@ const CopilotChatSuggestionView = React.forwardRef(function CopilotChatSuggestio
|
|
|
4170
5454
|
});
|
|
4171
5455
|
CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
|
|
4172
5456
|
|
|
5457
|
+
//#endregion
|
|
5458
|
+
//#region src/v2/components/chat/scroll-element-context.ts
|
|
5459
|
+
/**
|
|
5460
|
+
* Provides the scroll container element to child components that need it for
|
|
5461
|
+
* virtualization. Set by CopilotChatView.ScrollView; consumed by
|
|
5462
|
+
* CopilotChatMessageView to feed useVirtualizer's getScrollElement.
|
|
5463
|
+
*
|
|
5464
|
+
* Carries the element itself (not a ref) so that context consumers re-render
|
|
5465
|
+
* reactively when the scroll container is first mounted.
|
|
5466
|
+
*/
|
|
5467
|
+
const ScrollElementContext = React.createContext(null);
|
|
5468
|
+
|
|
4173
5469
|
//#endregion
|
|
4174
5470
|
//#region src/v2/components/chat/CopilotChatMessageView.tsx
|
|
4175
5471
|
/**
|
|
5472
|
+
* Resolves a slot value into a { Component, slotProps } pair, handling the three
|
|
5473
|
+
* slot forms: a component type, a className string, or a partial-props object.
|
|
5474
|
+
*/
|
|
5475
|
+
function resolveSlotComponent(slot, DefaultComponent) {
|
|
5476
|
+
if (isReactComponentType(slot)) return {
|
|
5477
|
+
Component: slot,
|
|
5478
|
+
slotProps: void 0
|
|
5479
|
+
};
|
|
5480
|
+
if (typeof slot === "string") return {
|
|
5481
|
+
Component: DefaultComponent,
|
|
5482
|
+
slotProps: { className: slot }
|
|
5483
|
+
};
|
|
5484
|
+
if (slot && typeof slot === "object") return {
|
|
5485
|
+
Component: DefaultComponent,
|
|
5486
|
+
slotProps: slot
|
|
5487
|
+
};
|
|
5488
|
+
return {
|
|
5489
|
+
Component: DefaultComponent,
|
|
5490
|
+
slotProps: void 0
|
|
5491
|
+
};
|
|
5492
|
+
}
|
|
5493
|
+
/**
|
|
4176
5494
|
* Memoized wrapper for assistant messages to prevent re-renders when other messages change.
|
|
4177
5495
|
*/
|
|
4178
5496
|
const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
|
|
@@ -4191,7 +5509,6 @@ const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({
|
|
|
4191
5509
|
if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
|
|
4192
5510
|
const prevTc = prevToolCalls[i];
|
|
4193
5511
|
const nextTc = nextToolCalls[i];
|
|
4194
|
-
if (!prevTc || !nextTc) return false;
|
|
4195
5512
|
if (prevTc.id !== nextTc.id) return false;
|
|
4196
5513
|
if (prevTc.function.arguments !== nextTc.function.arguments) return false;
|
|
4197
5514
|
}
|
|
@@ -4270,6 +5587,7 @@ const MemoizedCustomMessage = React.memo(function MemoizedCustomMessage({ messag
|
|
|
4270
5587
|
if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
|
|
4271
5588
|
return true;
|
|
4272
5589
|
});
|
|
5590
|
+
const VIRTUALIZE_THRESHOLD = 50;
|
|
4273
5591
|
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
|
|
4274
5592
|
const renderCustomMessage = useRenderCustomMessages();
|
|
4275
5593
|
const { renderActivityMessage } = useRenderActivityMessage();
|
|
@@ -4303,9 +5621,34 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4303
5621
|
if (!resolvedRunId) return void 0;
|
|
4304
5622
|
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
|
|
4305
5623
|
};
|
|
4306
|
-
const deduplicatedMessages = [...new Map(messages.map((m) => [m.id, m])).values()];
|
|
5624
|
+
const deduplicatedMessages = useMemo(() => [...new Map(messages.map((m) => [m.id, m])).values()], [messages]);
|
|
4307
5625
|
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Deduplicated ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
|
|
4308
|
-
const
|
|
5626
|
+
const { Component: AssistantComponent, slotProps: assistantSlotProps } = useMemo(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
|
|
5627
|
+
const { Component: UserComponent, slotProps: userSlotProps } = useMemo(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
|
|
5628
|
+
const { Component: ReasoningComponent, slotProps: reasoningSlotProps } = useMemo(() => resolveSlotComponent(reasoningMessage, CopilotChatReasoningMessage_default), [reasoningMessage]);
|
|
5629
|
+
const scrollElementFromCtx = useContext(ScrollElementContext);
|
|
5630
|
+
const scrollElement = scrollElementFromCtx && scrollElementFromCtx.clientHeight > 0 ? scrollElementFromCtx : null;
|
|
5631
|
+
useEffect(() => {
|
|
5632
|
+
if (process.env.NODE_ENV !== "production" && scrollElementFromCtx && scrollElementFromCtx.clientHeight === 0) console.warn("[CopilotKit] Chat scroll container has clientHeight=0 — virtualization disabled. Ensure the chat is rendered in a visible container with a non-zero height.");
|
|
5633
|
+
}, [scrollElementFromCtx]);
|
|
5634
|
+
const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
|
|
5635
|
+
const virtualizer = useVirtualizer({
|
|
5636
|
+
count: shouldVirtualize ? deduplicatedMessages.length : 0,
|
|
5637
|
+
getScrollElement: () => scrollElement,
|
|
5638
|
+
estimateSize: () => 100,
|
|
5639
|
+
overscan: 5,
|
|
5640
|
+
measureElement: (el) => el?.getBoundingClientRect().height ?? 0,
|
|
5641
|
+
initialRect: {
|
|
5642
|
+
width: 0,
|
|
5643
|
+
height: 600
|
|
5644
|
+
}
|
|
5645
|
+
});
|
|
5646
|
+
const firstMessageId = deduplicatedMessages[0]?.id;
|
|
5647
|
+
useLayoutEffect(() => {
|
|
5648
|
+
if (!shouldVirtualize || !deduplicatedMessages.length) return;
|
|
5649
|
+
virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
|
|
5650
|
+
}, [shouldVirtualize, firstMessageId]);
|
|
5651
|
+
const renderMessageBlock = (message) => {
|
|
4309
5652
|
const elements = [];
|
|
4310
5653
|
const stateSnapshot = getStateSnapshotForMessage(message.id);
|
|
4311
5654
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
|
|
@@ -4314,58 +5657,38 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4314
5657
|
renderCustomMessage,
|
|
4315
5658
|
stateSnapshot
|
|
4316
5659
|
}, `${message.id}-custom-before`));
|
|
4317
|
-
if (message.role === "assistant") {
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
}, message.id));
|
|
4341
|
-
} else if (message.role === "activity") {
|
|
4342
|
-
const activityMsg = message;
|
|
4343
|
-
elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
|
|
4344
|
-
message: activityMsg,
|
|
4345
|
-
renderActivityMessage
|
|
4346
|
-
}, message.id));
|
|
4347
|
-
} else if (message.role === "reasoning") {
|
|
4348
|
-
let ReasoningComponent = CopilotChatReasoningMessage_default;
|
|
4349
|
-
let reasoningSlotProps;
|
|
4350
|
-
if (isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
|
|
4351
|
-
else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
|
|
4352
|
-
else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
|
|
4353
|
-
elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
|
|
4354
|
-
message,
|
|
4355
|
-
messages,
|
|
4356
|
-
isRunning,
|
|
4357
|
-
ReasoningMessageComponent: ReasoningComponent,
|
|
4358
|
-
slotProps: reasoningSlotProps
|
|
4359
|
-
}, message.id));
|
|
4360
|
-
}
|
|
5660
|
+
if (message.role === "assistant") elements.push(/* @__PURE__ */ jsx(MemoizedAssistantMessage, {
|
|
5661
|
+
message,
|
|
5662
|
+
messages,
|
|
5663
|
+
isRunning,
|
|
5664
|
+
AssistantMessageComponent: AssistantComponent,
|
|
5665
|
+
slotProps: assistantSlotProps
|
|
5666
|
+
}, message.id));
|
|
5667
|
+
else if (message.role === "user") elements.push(/* @__PURE__ */ jsx(MemoizedUserMessage, {
|
|
5668
|
+
message,
|
|
5669
|
+
UserMessageComponent: UserComponent,
|
|
5670
|
+
slotProps: userSlotProps
|
|
5671
|
+
}, message.id));
|
|
5672
|
+
else if (message.role === "activity") elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
|
|
5673
|
+
message,
|
|
5674
|
+
renderActivityMessage
|
|
5675
|
+
}, message.id));
|
|
5676
|
+
else if (message.role === "reasoning") elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
|
|
5677
|
+
message,
|
|
5678
|
+
messages,
|
|
5679
|
+
isRunning,
|
|
5680
|
+
ReasoningMessageComponent: ReasoningComponent,
|
|
5681
|
+
slotProps: reasoningSlotProps
|
|
5682
|
+
}, message.id));
|
|
4361
5683
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
|
|
4362
5684
|
message,
|
|
4363
5685
|
position: "after",
|
|
4364
5686
|
renderCustomMessage,
|
|
4365
5687
|
stateSnapshot
|
|
4366
5688
|
}, `${message.id}-custom-after`));
|
|
4367
|
-
return elements;
|
|
4368
|
-
}
|
|
5689
|
+
return elements.filter(Boolean);
|
|
5690
|
+
};
|
|
5691
|
+
const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
|
|
4369
5692
|
if (children) return /* @__PURE__ */ jsx("div", {
|
|
4370
5693
|
"data-copilotkit": true,
|
|
4371
5694
|
style: { display: "contents" },
|
|
@@ -4376,30 +5699,356 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4376
5699
|
interruptElement
|
|
4377
5700
|
})
|
|
4378
5701
|
});
|
|
4379
|
-
const lastMessage = messages[messages.length - 1];
|
|
4380
|
-
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
5702
|
+
const lastMessage = messages[messages.length - 1];
|
|
5703
|
+
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
5704
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5705
|
+
"data-copilotkit": true,
|
|
5706
|
+
"data-testid": "copilot-message-list",
|
|
5707
|
+
className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
5708
|
+
...props,
|
|
5709
|
+
children: [
|
|
5710
|
+
shouldVirtualize ? /* @__PURE__ */ jsx("div", {
|
|
5711
|
+
style: {
|
|
5712
|
+
height: virtualizer.getTotalSize(),
|
|
5713
|
+
position: "relative"
|
|
5714
|
+
},
|
|
5715
|
+
children: virtualizer.getVirtualItems().map((virtualItem) => {
|
|
5716
|
+
const message = deduplicatedMessages[virtualItem.index];
|
|
5717
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5718
|
+
"data-index": virtualItem.index,
|
|
5719
|
+
ref: virtualizer.measureElement,
|
|
5720
|
+
style: {
|
|
5721
|
+
position: "absolute",
|
|
5722
|
+
top: 0,
|
|
5723
|
+
left: 0,
|
|
5724
|
+
width: "100%",
|
|
5725
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
5726
|
+
},
|
|
5727
|
+
children: renderMessageBlock(message)
|
|
5728
|
+
}, message.id);
|
|
5729
|
+
})
|
|
5730
|
+
}) : messageElements,
|
|
5731
|
+
interruptElement,
|
|
5732
|
+
showCursor && /* @__PURE__ */ jsx("div", {
|
|
5733
|
+
className: "cpk:mt-2",
|
|
5734
|
+
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
|
|
5735
|
+
})
|
|
5736
|
+
]
|
|
5737
|
+
});
|
|
5738
|
+
}
|
|
5739
|
+
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
5740
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5741
|
+
"data-testid": "copilot-loading-cursor",
|
|
5742
|
+
className: twMerge("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
|
|
5743
|
+
...props
|
|
5744
|
+
});
|
|
5745
|
+
};
|
|
5746
|
+
|
|
5747
|
+
//#endregion
|
|
5748
|
+
//#region src/v2/components/chat/CopilotChatAttachmentQueue.tsx
|
|
5749
|
+
const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
|
|
5750
|
+
if (attachments.length === 0) return null;
|
|
5751
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5752
|
+
className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
|
|
5753
|
+
children: attachments.map((attachment) => {
|
|
5754
|
+
const isMedia = attachment.type === "image" || attachment.type === "video";
|
|
5755
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5756
|
+
className: cn("cpk:relative cpk:inline-flex cpk:rounded-lg cpk:overflow-hidden cpk:border cpk:border-border", isMedia ? "cpk:w-[72px] cpk:h-[72px]" : attachment.type === "audio" ? "cpk:min-w-[200px] cpk:max-w-[280px] cpk:flex-col cpk:p-1 cpk:pr-8" : "cpk:p-2 cpk:px-3 cpk:pr-8 cpk:max-w-[240px]"),
|
|
5757
|
+
children: [
|
|
5758
|
+
attachment.status === "uploading" && /* @__PURE__ */ jsx(UploadingOverlay, {}),
|
|
5759
|
+
/* @__PURE__ */ jsx(AttachmentPreview, { attachment }),
|
|
5760
|
+
/* @__PURE__ */ jsx("button", {
|
|
5761
|
+
onClick: () => onRemoveAttachment(attachment.id),
|
|
5762
|
+
className: cn("cpk:absolute cpk:bg-black/60 cpk:text-white cpk:border-none cpk:rounded-full cpk:w-5 cpk:h-5 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:text-[10px] cpk:z-20", isMedia ? "cpk:top-1 cpk:right-1" : "cpk:top-1.5 cpk:right-1.5"),
|
|
5763
|
+
"aria-label": "Remove attachment",
|
|
5764
|
+
children: "✕"
|
|
5765
|
+
})
|
|
5766
|
+
]
|
|
5767
|
+
}, attachment.id);
|
|
5768
|
+
})
|
|
5769
|
+
});
|
|
5770
|
+
};
|
|
5771
|
+
function UploadingOverlay() {
|
|
5772
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5773
|
+
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10",
|
|
5774
|
+
children: /* @__PURE__ */ jsx("div", { className: "cpk:w-5 cpk:h-5 cpk:border-2 cpk:border-white cpk:border-t-transparent cpk:rounded-full cpk:animate-spin" })
|
|
5775
|
+
});
|
|
5776
|
+
}
|
|
5777
|
+
function AttachmentPreview({ attachment }) {
|
|
5778
|
+
if (attachment.status === "uploading") return /* @__PURE__ */ jsx("div", { className: "cpk:w-full cpk:h-full" });
|
|
5779
|
+
switch (attachment.type) {
|
|
5780
|
+
case "image": return /* @__PURE__ */ jsx(ImagePreview, { attachment });
|
|
5781
|
+
case "audio": return /* @__PURE__ */ jsx(AudioPreview, { attachment });
|
|
5782
|
+
case "video": return /* @__PURE__ */ jsx(VideoPreview, { attachment });
|
|
5783
|
+
case "document": return /* @__PURE__ */ jsx(DocumentPreview, { attachment });
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5786
|
+
function Lightbox({ onClose, children }) {
|
|
5787
|
+
useEffect(() => {
|
|
5788
|
+
const handleKey = (e) => {
|
|
5789
|
+
if (e.key === "Escape") onClose();
|
|
5790
|
+
};
|
|
5791
|
+
document.addEventListener("keydown", handleKey);
|
|
5792
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
5793
|
+
}, [onClose]);
|
|
5794
|
+
if (typeof document === "undefined") return null;
|
|
5795
|
+
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
5796
|
+
className: "cpk:fixed cpk:inset-0 cpk:z-[9999] cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/80 cpk:animate-fade-in",
|
|
5797
|
+
onClick: onClose,
|
|
5798
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
5799
|
+
onClick: onClose,
|
|
5800
|
+
className: "cpk:absolute cpk:top-4 cpk:right-4 cpk:text-white cpk:bg-white/10 cpk:hover:bg-white/20 cpk:rounded-full cpk:w-10 cpk:h-10 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:border-none cpk:z-10",
|
|
5801
|
+
"aria-label": "Close preview",
|
|
5802
|
+
children: /* @__PURE__ */ jsx(X, { className: "cpk:w-5 cpk:h-5" })
|
|
5803
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
5804
|
+
onClick: (e) => e.stopPropagation(),
|
|
5805
|
+
children
|
|
5806
|
+
})]
|
|
5807
|
+
}), document.body);
|
|
5808
|
+
}
|
|
5809
|
+
/**
|
|
5810
|
+
* Hook that manages lightbox open/close and uses the View Transition API to
|
|
5811
|
+
* morph the thumbnail into fullscreen content.
|
|
5812
|
+
*
|
|
5813
|
+
* The trick: `view-transition-name` must live on exactly ONE element at a time.
|
|
5814
|
+
* - Old state (thumbnail visible): name is on the thumbnail.
|
|
5815
|
+
* - New state (lightbox visible): name moves to the lightbox content.
|
|
5816
|
+
* `flushSync` ensures React commits the DOM change synchronously inside the
|
|
5817
|
+
* `startViewTransition` callback so the API can snapshot old → new correctly.
|
|
5818
|
+
*/
|
|
5819
|
+
function useLightbox() {
|
|
5820
|
+
const thumbnailRef = useRef(null);
|
|
5821
|
+
const [open, setOpen] = useState(false);
|
|
5822
|
+
const vtName = useId();
|
|
5823
|
+
return {
|
|
5824
|
+
thumbnailRef,
|
|
5825
|
+
vtName,
|
|
5826
|
+
open,
|
|
5827
|
+
openLightbox: useCallback(() => {
|
|
5828
|
+
const thumb = thumbnailRef.current;
|
|
5829
|
+
const doc = document;
|
|
5830
|
+
if (doc.startViewTransition && thumb) {
|
|
5831
|
+
thumb.style.viewTransitionName = vtName;
|
|
5832
|
+
doc.startViewTransition(() => {
|
|
5833
|
+
thumb.style.viewTransitionName = "";
|
|
5834
|
+
flushSync(() => setOpen(true));
|
|
5835
|
+
});
|
|
5836
|
+
} else setOpen(true);
|
|
5837
|
+
}, []),
|
|
5838
|
+
closeLightbox: useCallback(() => {
|
|
5839
|
+
const thumb = thumbnailRef.current;
|
|
5840
|
+
const doc = document;
|
|
5841
|
+
if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
|
|
5842
|
+
flushSync(() => setOpen(false));
|
|
5843
|
+
thumb.style.viewTransitionName = vtName;
|
|
5844
|
+
}).finished.then(() => {
|
|
5845
|
+
thumb.style.viewTransitionName = "";
|
|
5846
|
+
}).catch(() => {
|
|
5847
|
+
thumb.style.viewTransitionName = "";
|
|
5848
|
+
});
|
|
5849
|
+
else setOpen(false);
|
|
5850
|
+
}, [])
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
function ImagePreview({ attachment }) {
|
|
5854
|
+
const src = getSourceUrl(attachment.source);
|
|
5855
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5856
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("img", {
|
|
5857
|
+
ref: thumbnailRef,
|
|
5858
|
+
src,
|
|
5859
|
+
alt: attachment.filename || "Image attachment",
|
|
5860
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover cpk:cursor-pointer",
|
|
5861
|
+
onClick: openLightbox
|
|
5862
|
+
}), open && /* @__PURE__ */ jsx(Lightbox, {
|
|
5863
|
+
onClose: closeLightbox,
|
|
5864
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
5865
|
+
style: { viewTransitionName: vtName },
|
|
5866
|
+
src,
|
|
5867
|
+
alt: attachment.filename || "Image attachment",
|
|
5868
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
|
|
5869
|
+
})
|
|
5870
|
+
})] });
|
|
5871
|
+
}
|
|
5872
|
+
function AudioPreview({ attachment }) {
|
|
5873
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5874
|
+
className: "cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full",
|
|
5875
|
+
children: [/* @__PURE__ */ jsx("audio", {
|
|
5876
|
+
src: getSourceUrl(attachment.source),
|
|
5877
|
+
controls: true,
|
|
5878
|
+
preload: "metadata",
|
|
5879
|
+
className: "cpk:w-full cpk:h-8"
|
|
5880
|
+
}), attachment.filename && /* @__PURE__ */ jsx("span", {
|
|
5881
|
+
className: "cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap",
|
|
5882
|
+
children: attachment.filename
|
|
5883
|
+
})]
|
|
5884
|
+
});
|
|
5885
|
+
}
|
|
5886
|
+
function VideoPreview({ attachment }) {
|
|
5887
|
+
const src = getSourceUrl(attachment.source);
|
|
5888
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5889
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
5890
|
+
/* @__PURE__ */ jsx("div", {
|
|
5891
|
+
ref: thumbnailRef,
|
|
5892
|
+
className: "cpk:w-full cpk:h-full",
|
|
5893
|
+
children: attachment.thumbnail ? /* @__PURE__ */ jsx("img", {
|
|
5894
|
+
src: attachment.thumbnail,
|
|
5895
|
+
alt: attachment.filename || "Video thumbnail",
|
|
5896
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover"
|
|
5897
|
+
}) : /* @__PURE__ */ jsx("video", {
|
|
5898
|
+
src,
|
|
5899
|
+
preload: "metadata",
|
|
5900
|
+
muted: true,
|
|
5901
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover"
|
|
5902
|
+
})
|
|
5903
|
+
}),
|
|
5904
|
+
/* @__PURE__ */ jsx("button", {
|
|
5905
|
+
onClick: openLightbox,
|
|
5906
|
+
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:z-10 cpk:cursor-pointer cpk:bg-black/20 cpk:border-none cpk:p-0",
|
|
5907
|
+
"aria-label": "Play video",
|
|
5908
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
5909
|
+
className: "cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center",
|
|
5910
|
+
children: /* @__PURE__ */ jsx(Play, { className: "cpk:w-4 cpk:h-4 cpk:text-white cpk:ml-0.5" })
|
|
5911
|
+
})
|
|
5912
|
+
}),
|
|
5913
|
+
open && /* @__PURE__ */ jsx(Lightbox, {
|
|
5914
|
+
onClose: closeLightbox,
|
|
5915
|
+
children: /* @__PURE__ */ jsx("video", {
|
|
5916
|
+
style: { viewTransitionName: vtName },
|
|
5917
|
+
src,
|
|
5918
|
+
controls: true,
|
|
5919
|
+
autoPlay: true,
|
|
5920
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
|
|
5921
|
+
})
|
|
5922
|
+
})
|
|
5923
|
+
] });
|
|
5924
|
+
}
|
|
5925
|
+
function isPdf(mimeType) {
|
|
5926
|
+
return !!mimeType && mimeType.includes("pdf");
|
|
5927
|
+
}
|
|
5928
|
+
function isText(mimeType) {
|
|
5929
|
+
return !!mimeType && mimeType.startsWith("text/");
|
|
5930
|
+
}
|
|
5931
|
+
function canPreviewInBrowser(mimeType) {
|
|
5932
|
+
return isPdf(mimeType) || isText(mimeType);
|
|
5933
|
+
}
|
|
5934
|
+
/**
|
|
5935
|
+
* Convert a base64-encoded data source to a blob: URL that browsers will
|
|
5936
|
+
* render inside an iframe (data: URLs are blocked for PDFs in most browsers).
|
|
5937
|
+
*/
|
|
5938
|
+
function useBlobUrl(attachment) {
|
|
5939
|
+
const [url, setUrl] = useState(null);
|
|
5940
|
+
useEffect(() => {
|
|
5941
|
+
if (attachment.source.type !== "data") return;
|
|
5942
|
+
try {
|
|
5943
|
+
const binary = atob(attachment.source.value);
|
|
5944
|
+
const bytes = new Uint8Array(binary.length);
|
|
5945
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
5946
|
+
const blob = new Blob([bytes], { type: attachment.source.mimeType || "application/octet-stream" });
|
|
5947
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
5948
|
+
setUrl(blobUrl);
|
|
5949
|
+
return () => URL.revokeObjectURL(blobUrl);
|
|
5950
|
+
} catch (error) {
|
|
5951
|
+
console.error("[CopilotKit] Failed to decode attachment data:", error);
|
|
5952
|
+
setUrl(null);
|
|
5953
|
+
}
|
|
5954
|
+
}, [
|
|
5955
|
+
attachment.source.type,
|
|
5956
|
+
attachment.source.value,
|
|
5957
|
+
attachment.source.mimeType
|
|
5958
|
+
]);
|
|
5959
|
+
if (attachment.source.type === "url") return attachment.source.value;
|
|
5960
|
+
return url;
|
|
5961
|
+
}
|
|
5962
|
+
function DocumentLightboxContent({ attachment, vtName }) {
|
|
5963
|
+
const mimeType = attachment.source.mimeType;
|
|
5964
|
+
const blobUrl = useBlobUrl(attachment);
|
|
5965
|
+
if (isPdf(mimeType)) {
|
|
5966
|
+
if (!blobUrl) return null;
|
|
5967
|
+
return /* @__PURE__ */ jsx("iframe", {
|
|
5968
|
+
style: { viewTransitionName: vtName },
|
|
5969
|
+
src: blobUrl,
|
|
5970
|
+
title: attachment.filename || "PDF preview",
|
|
5971
|
+
className: "cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
|
|
5972
|
+
});
|
|
5973
|
+
}
|
|
5974
|
+
if (isText(mimeType)) {
|
|
5975
|
+
const textContent = attachment.source.type === "data" ? (() => {
|
|
5976
|
+
try {
|
|
5977
|
+
return atob(attachment.source.value);
|
|
5978
|
+
} catch {
|
|
5979
|
+
return attachment.source.value;
|
|
5980
|
+
}
|
|
5981
|
+
})() : null;
|
|
5982
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5983
|
+
style: { viewTransitionName: vtName },
|
|
5984
|
+
className: "cpk:w-[90vw] cpk:max-w-[800px] cpk:max-h-[90vh] cpk:overflow-auto cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900 cpk:p-6",
|
|
5985
|
+
children: [attachment.filename && /* @__PURE__ */ jsx("div", {
|
|
5986
|
+
className: "cpk:text-sm cpk:font-medium cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mb-4 cpk:pb-2 cpk:border-b cpk:border-gray-200 cpk:dark:border-gray-700",
|
|
5987
|
+
children: attachment.filename
|
|
5988
|
+
}), textContent ? /* @__PURE__ */ jsx("pre", {
|
|
5989
|
+
className: "cpk:text-sm cpk:whitespace-pre-wrap cpk:break-words cpk:text-gray-800 cpk:dark:text-gray-200 cpk:font-mono cpk:m-0",
|
|
5990
|
+
children: textContent
|
|
5991
|
+
}) : blobUrl ? /* @__PURE__ */ jsx("iframe", {
|
|
5992
|
+
src: blobUrl,
|
|
5993
|
+
title: attachment.filename || "Text preview",
|
|
5994
|
+
className: "cpk:w-full cpk:h-[80vh] cpk:border-none"
|
|
5995
|
+
}) : null]
|
|
5996
|
+
});
|
|
5997
|
+
}
|
|
4381
5998
|
return /* @__PURE__ */ jsxs("div", {
|
|
4382
|
-
|
|
4383
|
-
"
|
|
4384
|
-
className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
4385
|
-
...props,
|
|
5999
|
+
style: { viewTransitionName: vtName },
|
|
6000
|
+
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-4 cpk:p-8 cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900",
|
|
4386
6001
|
children: [
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
6002
|
+
/* @__PURE__ */ jsx("div", {
|
|
6003
|
+
className: "cpk:w-16 cpk:h-16 cpk:rounded-xl cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-xl cpk:font-bold",
|
|
6004
|
+
children: getDocumentIcon(mimeType ?? "")
|
|
6005
|
+
}),
|
|
6006
|
+
/* @__PURE__ */ jsxs("div", {
|
|
6007
|
+
className: "cpk:text-center",
|
|
6008
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
6009
|
+
className: "cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200",
|
|
6010
|
+
children: attachment.filename || "Document"
|
|
6011
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
6012
|
+
className: "cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1",
|
|
6013
|
+
children: [mimeType || "Unknown type", attachment.size != null && ` · ${formatFileSize(attachment.size)}`]
|
|
6014
|
+
})]
|
|
6015
|
+
}),
|
|
6016
|
+
/* @__PURE__ */ jsx("div", {
|
|
6017
|
+
className: "cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500",
|
|
6018
|
+
children: "No preview available for this file type"
|
|
4392
6019
|
})
|
|
4393
6020
|
]
|
|
4394
6021
|
});
|
|
4395
6022
|
}
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
6023
|
+
function DocumentPreview({ attachment }) {
|
|
6024
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
6025
|
+
const mimeType = attachment.source.mimeType;
|
|
6026
|
+
const previewable = canPreviewInBrowser(mimeType);
|
|
6027
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
6028
|
+
ref: thumbnailRef,
|
|
6029
|
+
className: cn("cpk:flex cpk:items-center cpk:gap-2", previewable && "cpk:cursor-pointer"),
|
|
6030
|
+
onClick: previewable ? openLightbox : void 0,
|
|
6031
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
6032
|
+
className: "cpk:w-8 cpk:h-8 cpk:rounded-md cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-[10px] cpk:font-semibold cpk:shrink-0",
|
|
6033
|
+
children: getDocumentIcon(mimeType ?? "")
|
|
6034
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
6035
|
+
className: "cpk:flex cpk:flex-col cpk:min-w-0",
|
|
6036
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
6037
|
+
className: "cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight",
|
|
6038
|
+
children: attachment.filename || "Document"
|
|
6039
|
+
}), attachment.size != null && /* @__PURE__ */ jsx("span", {
|
|
6040
|
+
className: "cpk:text-[11px] cpk:text-muted-foreground",
|
|
6041
|
+
children: formatFileSize(attachment.size)
|
|
6042
|
+
})]
|
|
6043
|
+
})]
|
|
6044
|
+
}), open && /* @__PURE__ */ jsx(Lightbox, {
|
|
6045
|
+
onClose: closeLightbox,
|
|
6046
|
+
children: /* @__PURE__ */ jsx(DocumentLightboxContent, {
|
|
6047
|
+
attachment,
|
|
6048
|
+
vtName
|
|
6049
|
+
})
|
|
6050
|
+
})] });
|
|
6051
|
+
}
|
|
4403
6052
|
|
|
4404
6053
|
//#endregion
|
|
4405
6054
|
//#region src/v2/hooks/use-keyboard-height.tsx
|
|
@@ -4445,7 +6094,19 @@ function useKeyboardHeight() {
|
|
|
4445
6094
|
//#endregion
|
|
4446
6095
|
//#region src/v2/components/chat/CopilotChatView.tsx
|
|
4447
6096
|
const FEATHER_HEIGHT = 96;
|
|
4448
|
-
function
|
|
6097
|
+
function DropOverlay() {
|
|
6098
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6099
|
+
className: cn("cpk:absolute cpk:inset-0 cpk:z-50 cpk:pointer-events-none", "cpk:flex cpk:items-center cpk:justify-center", "cpk:bg-primary/5 cpk:backdrop-blur-[2px]", "cpk:border-2 cpk:border-dashed cpk:border-primary/40 cpk:rounded-lg cpk:m-2"),
|
|
6100
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
6101
|
+
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-2 cpk:text-primary/70",
|
|
6102
|
+
children: [/* @__PURE__ */ jsx(Upload, { className: "cpk:w-8 cpk:h-8" }), /* @__PURE__ */ jsx("span", {
|
|
6103
|
+
className: "cpk:text-sm cpk:font-medium",
|
|
6104
|
+
children: "Drop files here"
|
|
6105
|
+
})]
|
|
6106
|
+
})
|
|
6107
|
+
});
|
|
6108
|
+
}
|
|
6109
|
+
function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, disclaimer, children, className, ...props }) {
|
|
4449
6110
|
const inputContainerRef = useRef(null);
|
|
4450
6111
|
const [inputContainerHeight, setInputContainerHeight] = useState(0);
|
|
4451
6112
|
const [isResizing, setIsResizing] = useState(false);
|
|
@@ -4492,6 +6153,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4492
6153
|
onCancelTranscribe,
|
|
4493
6154
|
onFinishTranscribe,
|
|
4494
6155
|
onFinishTranscribeWithAudio,
|
|
6156
|
+
onAddFile,
|
|
4495
6157
|
positioning: "static",
|
|
4496
6158
|
keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
|
|
4497
6159
|
containerRef: inputContainerRef,
|
|
@@ -4532,21 +6194,34 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4532
6194
|
onCancelTranscribe,
|
|
4533
6195
|
onFinishTranscribe,
|
|
4534
6196
|
onFinishTranscribeWithAudio,
|
|
6197
|
+
onAddFile,
|
|
4535
6198
|
positioning: "static",
|
|
4536
6199
|
showDisclaimer: true,
|
|
4537
6200
|
...disclaimer !== void 0 ? { disclaimer } : {}
|
|
4538
6201
|
});
|
|
4539
|
-
const
|
|
4540
|
-
|
|
6202
|
+
const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
|
|
6203
|
+
const inputWithAttachments = /* @__PURE__ */ jsxs("div", {
|
|
6204
|
+
className: "cpk:w-full",
|
|
6205
|
+
children: [attachments && attachments.length > 0 && /* @__PURE__ */ jsx(CopilotChatAttachmentQueue, {
|
|
6206
|
+
attachments,
|
|
6207
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6208
|
+
className: "cpk:mb-2"
|
|
6209
|
+
}), BoundInputForWelcome]
|
|
6210
|
+
});
|
|
6211
|
+
const BoundWelcomeScreen = renderSlot(welcomeScreenSlot, CopilotChatView.WelcomeScreen, {
|
|
6212
|
+
input: inputWithAttachments,
|
|
4541
6213
|
suggestionView: BoundSuggestionView ?? /* @__PURE__ */ jsx(Fragment$1, {})
|
|
4542
6214
|
});
|
|
4543
|
-
return /* @__PURE__ */
|
|
6215
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4544
6216
|
"data-copilotkit": true,
|
|
4545
6217
|
"data-testid": "copilot-chat",
|
|
4546
6218
|
"data-copilot-running": isRunning ? "true" : "false",
|
|
4547
|
-
|
|
6219
|
+
onDragOver,
|
|
6220
|
+
onDragLeave,
|
|
6221
|
+
onDrop,
|
|
6222
|
+
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
4548
6223
|
...props,
|
|
4549
|
-
children: BoundWelcomeScreen
|
|
6224
|
+
children: [dragOver && /* @__PURE__ */ jsx(DropOverlay, {}), BoundWelcomeScreen]
|
|
4550
6225
|
});
|
|
4551
6226
|
}
|
|
4552
6227
|
if (children) return /* @__PURE__ */ jsx("div", {
|
|
@@ -4563,39 +6238,66 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4563
6238
|
"data-copilotkit": true,
|
|
4564
6239
|
"data-testid": "copilot-chat",
|
|
4565
6240
|
"data-copilot-running": isRunning ? "true" : "false",
|
|
4566
|
-
|
|
6241
|
+
onDragOver,
|
|
6242
|
+
onDragLeave,
|
|
6243
|
+
onDrop,
|
|
6244
|
+
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
4567
6245
|
...props,
|
|
4568
|
-
children: [
|
|
6246
|
+
children: [
|
|
6247
|
+
dragOver && /* @__PURE__ */ jsx(DropOverlay, {}),
|
|
6248
|
+
BoundScrollView,
|
|
6249
|
+
/* @__PURE__ */ jsx("div", {
|
|
6250
|
+
className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full",
|
|
6251
|
+
children: attachments && attachments.length > 0 && /* @__PURE__ */ jsx(CopilotChatAttachmentQueue, {
|
|
6252
|
+
attachments,
|
|
6253
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6254
|
+
className: "cpk:px-4"
|
|
6255
|
+
})
|
|
6256
|
+
}),
|
|
6257
|
+
BoundInput
|
|
6258
|
+
]
|
|
4569
6259
|
});
|
|
4570
6260
|
}
|
|
4571
6261
|
(function(_CopilotChatView) {
|
|
4572
6262
|
const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
|
|
4573
|
-
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
6263
|
+
const { isAtBottom, scrollToBottom, scrollRef } = useStickToBottomContext();
|
|
6264
|
+
const [scrollEl, setScrollEl] = useState(null);
|
|
6265
|
+
useLayoutEffect(() => {
|
|
6266
|
+
setScrollEl(scrollRef.current ?? null);
|
|
6267
|
+
}, []);
|
|
4574
6268
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
4575
|
-
return /* @__PURE__ */
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
children
|
|
6269
|
+
return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
|
|
6270
|
+
value: scrollEl,
|
|
6271
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
6272
|
+
/* @__PURE__ */ jsx(StickToBottom.Content, {
|
|
6273
|
+
className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
6274
|
+
style: {
|
|
6275
|
+
flex: "1 1 0%",
|
|
6276
|
+
minHeight: 0
|
|
6277
|
+
},
|
|
6278
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
6279
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6280
|
+
children
|
|
6281
|
+
})
|
|
6282
|
+
}),
|
|
6283
|
+
BoundFeather,
|
|
6284
|
+
!isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
|
|
6285
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6286
|
+
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
6287
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
4585
6288
|
})
|
|
4586
|
-
})
|
|
4587
|
-
|
|
4588
|
-
!isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
|
|
4589
|
-
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
4590
|
-
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
4591
|
-
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
4592
|
-
})
|
|
4593
|
-
] });
|
|
6289
|
+
] })
|
|
6290
|
+
});
|
|
4594
6291
|
};
|
|
4595
6292
|
_CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
|
|
4596
6293
|
const [hasMounted, setHasMounted] = useState(false);
|
|
4597
6294
|
const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
|
|
4598
6295
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
6296
|
+
const [nonAutoScrollEl, setNonAutoScrollEl] = useState(null);
|
|
6297
|
+
const nonAutoScrollRefCallback = useCallback((el) => {
|
|
6298
|
+
scrollRef.current = el;
|
|
6299
|
+
setNonAutoScrollEl(el);
|
|
6300
|
+
}, []);
|
|
4599
6301
|
useEffect(() => {
|
|
4600
6302
|
setHasMounted(true);
|
|
4601
6303
|
}, []);
|
|
@@ -4624,23 +6326,26 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4624
6326
|
});
|
|
4625
6327
|
if (!autoScroll) {
|
|
4626
6328
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
4627
|
-
return /* @__PURE__ */
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
6329
|
+
return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
|
|
6330
|
+
value: nonAutoScrollEl,
|
|
6331
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
6332
|
+
ref: nonAutoScrollRefCallback,
|
|
6333
|
+
className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden cpk:relative", className),
|
|
6334
|
+
...props,
|
|
6335
|
+
children: [
|
|
6336
|
+
/* @__PURE__ */ jsx("div", {
|
|
6337
|
+
ref: contentRef,
|
|
6338
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6339
|
+
children
|
|
6340
|
+
}),
|
|
6341
|
+
BoundFeather,
|
|
6342
|
+
showScrollButton && !isResizing && /* @__PURE__ */ jsx("div", {
|
|
6343
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6344
|
+
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
6345
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
6346
|
+
})
|
|
6347
|
+
]
|
|
6348
|
+
})
|
|
4644
6349
|
});
|
|
4645
6350
|
}
|
|
4646
6351
|
return /* @__PURE__ */ jsx(StickToBottom, {
|
|
@@ -4832,13 +6537,14 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
|
|
|
4832
6537
|
|
|
4833
6538
|
//#endregion
|
|
4834
6539
|
//#region src/v2/components/chat/CopilotChat.tsx
|
|
4835
|
-
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, onError, ...props }) {
|
|
6540
|
+
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
|
|
4836
6541
|
const existingConfig = useCopilotChatConfiguration();
|
|
4837
6542
|
const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
4838
6543
|
const resolvedThreadId = useMemo(() => threadId ?? existingConfig?.threadId ?? randomUUID(), [threadId, existingConfig?.threadId]);
|
|
4839
6544
|
const { agent } = useAgent({
|
|
4840
6545
|
agentId: resolvedAgentId,
|
|
4841
|
-
threadId: resolvedThreadId
|
|
6546
|
+
threadId: resolvedThreadId,
|
|
6547
|
+
throttleMs
|
|
4842
6548
|
});
|
|
4843
6549
|
const { copilotkit } = useCopilotKit();
|
|
4844
6550
|
const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
|
|
@@ -4868,6 +6574,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4868
6574
|
const [inputValue, setInputValue] = useState("");
|
|
4869
6575
|
const [transcriptionError, setTranscriptionError] = useState(null);
|
|
4870
6576
|
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
6577
|
+
const { attachments: selectedAttachments, enabled: attachmentsEnabled, dragOver, fileInputRef, containerRef: chatContainerRef, handleFileUpload, handleDragOver, handleDragLeave, handleDrop, removeAttachment, consumeAttachments } = useAttachments({ config: attachmentsConfig });
|
|
4871
6578
|
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
4872
6579
|
const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
|
|
4873
6580
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
@@ -4895,7 +6602,31 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4895
6602
|
resolvedAgentId
|
|
4896
6603
|
]);
|
|
4897
6604
|
const onSubmitInput = useCallback(async (value) => {
|
|
4898
|
-
|
|
6605
|
+
if (selectedAttachments.some((a) => a.status === "uploading")) {
|
|
6606
|
+
console.error("[CopilotKit] Cannot send while attachments are uploading");
|
|
6607
|
+
return;
|
|
6608
|
+
}
|
|
6609
|
+
const readyAttachments = consumeAttachments();
|
|
6610
|
+
if (readyAttachments.length > 0) {
|
|
6611
|
+
const contentParts = [];
|
|
6612
|
+
if (value.trim()) contentParts.push({
|
|
6613
|
+
type: "text",
|
|
6614
|
+
text: value
|
|
6615
|
+
});
|
|
6616
|
+
for (const att of readyAttachments) contentParts.push({
|
|
6617
|
+
type: att.type,
|
|
6618
|
+
source: att.source,
|
|
6619
|
+
metadata: {
|
|
6620
|
+
...att.filename ? { filename: att.filename } : {},
|
|
6621
|
+
...att.metadata
|
|
6622
|
+
}
|
|
6623
|
+
});
|
|
6624
|
+
agent.addMessage({
|
|
6625
|
+
id: randomUUID(),
|
|
6626
|
+
role: "user",
|
|
6627
|
+
content: contentParts
|
|
6628
|
+
});
|
|
6629
|
+
} else agent.addMessage({
|
|
4899
6630
|
id: randomUUID(),
|
|
4900
6631
|
role: "user",
|
|
4901
6632
|
content: value
|
|
@@ -4906,7 +6637,11 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4906
6637
|
} catch (error) {
|
|
4907
6638
|
console.error("CopilotChat: runAgent failed", error);
|
|
4908
6639
|
}
|
|
4909
|
-
}, [
|
|
6640
|
+
}, [
|
|
6641
|
+
agent,
|
|
6642
|
+
selectedAttachments,
|
|
6643
|
+
consumeAttachments
|
|
6644
|
+
]);
|
|
4910
6645
|
const handleSelectSuggestion = useCallback(async (suggestion) => {
|
|
4911
6646
|
agent.addMessage({
|
|
4912
6647
|
id: randomUUID(),
|
|
@@ -4993,21 +6728,33 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4993
6728
|
return () => clearTimeout(timer);
|
|
4994
6729
|
}
|
|
4995
6730
|
}, [transcriptionError]);
|
|
4996
|
-
const
|
|
6731
|
+
const stableMessageView = useShallowStableRef(typeof providedMessageView === "string" ? { className: providedMessageView } : providedMessageView);
|
|
6732
|
+
const stableSuggestionView = useShallowStableRef(providedSuggestionView);
|
|
6733
|
+
const handleAddFile = useCallback(() => {
|
|
6734
|
+
setTimeout(() => {
|
|
6735
|
+
fileInputRef.current?.click();
|
|
6736
|
+
}, 100);
|
|
6737
|
+
}, []);
|
|
6738
|
+
const mergedProps = {
|
|
4997
6739
|
isRunning: agent.isRunning,
|
|
4998
6740
|
suggestions: autoSuggestions,
|
|
4999
6741
|
onSelectSuggestion: handleSelectSuggestion,
|
|
5000
|
-
suggestionView:
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
});
|
|
6742
|
+
suggestionView: stableSuggestionView,
|
|
6743
|
+
...restProps
|
|
6744
|
+
};
|
|
6745
|
+
if (stableMessageView !== void 0) mergedProps.messageView = stableMessageView;
|
|
5005
6746
|
const hasMessages = agent.messages.length > 0;
|
|
5006
6747
|
const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
|
|
5007
6748
|
const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
|
|
5008
6749
|
const effectiveMode = isTranscribing ? "processing" : transcribeMode;
|
|
5009
|
-
const
|
|
5010
|
-
|
|
6750
|
+
const messages = useMemo(() => [...agent.messages], [agent.messages.map((m) => {
|
|
6751
|
+
const contentKey = typeof m.content === "string" ? m.content.length : Array.isArray(m.content) ? m.content.length : 0;
|
|
6752
|
+
const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
|
|
6753
|
+
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
|
|
6754
|
+
}).join(",")]);
|
|
6755
|
+
const RenderedChatView = renderSlot(chatView, CopilotChatView, {
|
|
6756
|
+
...mergedProps,
|
|
6757
|
+
messages,
|
|
5011
6758
|
onSubmitMessage: onSubmitInput,
|
|
5012
6759
|
onStop: effectiveStopHandler,
|
|
5013
6760
|
inputMode: effectiveMode,
|
|
@@ -5016,32 +6763,51 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
5016
6763
|
onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
|
|
5017
6764
|
onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
|
|
5018
6765
|
onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
|
|
5019
|
-
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
|
|
5020
|
-
|
|
5021
|
-
|
|
6766
|
+
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0,
|
|
6767
|
+
attachments: selectedAttachments,
|
|
6768
|
+
onRemoveAttachment: removeAttachment,
|
|
6769
|
+
onAddFile: attachmentsEnabled ? handleAddFile : void 0,
|
|
6770
|
+
dragOver,
|
|
6771
|
+
onDragOver: handleDragOver,
|
|
6772
|
+
onDragLeave: handleDragLeave,
|
|
6773
|
+
onDrop: handleDrop
|
|
6774
|
+
});
|
|
6775
|
+
return /* @__PURE__ */ jsx(CopilotChatConfigurationProvider, {
|
|
5022
6776
|
agentId: resolvedAgentId,
|
|
5023
6777
|
threadId: resolvedThreadId,
|
|
5024
6778
|
labels,
|
|
5025
6779
|
isModalDefaultOpen,
|
|
5026
|
-
children:
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
6780
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
6781
|
+
ref: chatContainerRef,
|
|
6782
|
+
style: { display: "contents" },
|
|
6783
|
+
children: [
|
|
6784
|
+
attachmentsEnabled && /* @__PURE__ */ jsx("input", {
|
|
6785
|
+
type: "file",
|
|
6786
|
+
multiple: true,
|
|
6787
|
+
ref: fileInputRef,
|
|
6788
|
+
onChange: handleFileUpload,
|
|
6789
|
+
accept: attachmentsConfig?.accept ?? "*/*",
|
|
6790
|
+
style: { display: "none" }
|
|
6791
|
+
}),
|
|
6792
|
+
!isChatLicensed && /* @__PURE__ */ jsx(InlineFeatureWarning, { featureName: "Chat" }),
|
|
6793
|
+
transcriptionError && /* @__PURE__ */ jsx("div", {
|
|
6794
|
+
style: {
|
|
6795
|
+
position: "absolute",
|
|
6796
|
+
bottom: "100px",
|
|
6797
|
+
left: "50%",
|
|
6798
|
+
transform: "translateX(-50%)",
|
|
6799
|
+
backgroundColor: "#ef4444",
|
|
6800
|
+
color: "white",
|
|
6801
|
+
padding: "8px 16px",
|
|
6802
|
+
borderRadius: "8px",
|
|
6803
|
+
fontSize: "14px",
|
|
6804
|
+
zIndex: 50
|
|
6805
|
+
},
|
|
6806
|
+
children: transcriptionError
|
|
6807
|
+
}),
|
|
6808
|
+
RenderedChatView
|
|
6809
|
+
]
|
|
6810
|
+
})
|
|
5045
6811
|
});
|
|
5046
6812
|
}
|
|
5047
6813
|
(function(_CopilotChat) {
|
|
@@ -5906,6 +7672,227 @@ function useToast() {
|
|
|
5906
7672
|
if (!context) throw new Error("useToast must be used within a ToastProvider");
|
|
5907
7673
|
return context;
|
|
5908
7674
|
}
|
|
7675
|
+
function formatBannerMessage(message) {
|
|
7676
|
+
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
|
|
7677
|
+
if (jsonMatch) return jsonMatch[1];
|
|
7678
|
+
let cleaned = message.split(" - ")[0];
|
|
7679
|
+
cleaned = cleaned.split(": Error code")[0];
|
|
7680
|
+
cleaned = cleaned.replace(/:\s*\d{3}$/, "");
|
|
7681
|
+
cleaned = cleaned.replace(/See more:.*$/g, "");
|
|
7682
|
+
cleaned = cleaned.trim();
|
|
7683
|
+
return cleaned || "An error occurred.";
|
|
7684
|
+
}
|
|
7685
|
+
function extractUrl(message) {
|
|
7686
|
+
const markdownMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(message);
|
|
7687
|
+
if (markdownMatch) return {
|
|
7688
|
+
url: markdownMatch[2],
|
|
7689
|
+
text: "See More"
|
|
7690
|
+
};
|
|
7691
|
+
const plainMatch = /(https?:\/\/[^\s)]+)/.exec(message);
|
|
7692
|
+
if (plainMatch) return {
|
|
7693
|
+
url: plainMatch[0].replace(/[.,;:'"]*$/, ""),
|
|
7694
|
+
text: "See More"
|
|
7695
|
+
};
|
|
7696
|
+
return null;
|
|
7697
|
+
}
|
|
7698
|
+
function BannerErrorDisplay({ bannerError, onDismiss }) {
|
|
7699
|
+
const [detailsExpanded, setDetailsExpanded] = useState(false);
|
|
7700
|
+
const colors = getErrorColors(getErrorSeverity(bannerError));
|
|
7701
|
+
const details = bannerError.details;
|
|
7702
|
+
const link = extractUrl(bannerError.message);
|
|
7703
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
7704
|
+
style: {
|
|
7705
|
+
position: "fixed",
|
|
7706
|
+
bottom: "20px",
|
|
7707
|
+
left: "50%",
|
|
7708
|
+
transform: "translateX(-50%)",
|
|
7709
|
+
zIndex: 9999,
|
|
7710
|
+
backgroundColor: colors.background,
|
|
7711
|
+
border: `1px solid ${colors.border}`,
|
|
7712
|
+
borderLeft: `4px solid ${colors.border}`,
|
|
7713
|
+
borderRadius: "8px",
|
|
7714
|
+
padding: "12px 16px",
|
|
7715
|
+
fontSize: "13px",
|
|
7716
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
7717
|
+
backdropFilter: "blur(8px)",
|
|
7718
|
+
maxWidth: "min(90vw, 700px)",
|
|
7719
|
+
width: "100%",
|
|
7720
|
+
boxSizing: "border-box",
|
|
7721
|
+
overflow: "hidden"
|
|
7722
|
+
},
|
|
7723
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
7724
|
+
style: {
|
|
7725
|
+
display: "flex",
|
|
7726
|
+
justifyContent: "space-between",
|
|
7727
|
+
alignItems: "center",
|
|
7728
|
+
gap: "10px"
|
|
7729
|
+
},
|
|
7730
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
7731
|
+
style: {
|
|
7732
|
+
display: "flex",
|
|
7733
|
+
alignItems: "center",
|
|
7734
|
+
gap: "8px",
|
|
7735
|
+
flex: 1,
|
|
7736
|
+
minWidth: 0
|
|
7737
|
+
},
|
|
7738
|
+
children: [/* @__PURE__ */ jsx("div", { style: {
|
|
7739
|
+
width: "12px",
|
|
7740
|
+
height: "12px",
|
|
7741
|
+
borderRadius: "50%",
|
|
7742
|
+
backgroundColor: colors.border,
|
|
7743
|
+
flexShrink: 0
|
|
7744
|
+
} }), /* @__PURE__ */ jsxs("div", {
|
|
7745
|
+
style: {
|
|
7746
|
+
display: "flex",
|
|
7747
|
+
alignItems: "center",
|
|
7748
|
+
gap: "10px",
|
|
7749
|
+
flex: 1,
|
|
7750
|
+
minWidth: 0
|
|
7751
|
+
},
|
|
7752
|
+
children: [
|
|
7753
|
+
/* @__PURE__ */ jsx("div", {
|
|
7754
|
+
style: {
|
|
7755
|
+
color: colors.text,
|
|
7756
|
+
lineHeight: "1.4",
|
|
7757
|
+
fontWeight: "400",
|
|
7758
|
+
fontSize: "13px",
|
|
7759
|
+
flex: 1,
|
|
7760
|
+
wordBreak: "break-all",
|
|
7761
|
+
overflowWrap: "break-word",
|
|
7762
|
+
maxWidth: "550px",
|
|
7763
|
+
overflow: "hidden",
|
|
7764
|
+
display: "-webkit-box",
|
|
7765
|
+
WebkitLineClamp: 10,
|
|
7766
|
+
WebkitBoxOrient: "vertical"
|
|
7767
|
+
},
|
|
7768
|
+
children: formatBannerMessage(bannerError.message)
|
|
7769
|
+
}),
|
|
7770
|
+
link && /* @__PURE__ */ jsx("button", {
|
|
7771
|
+
onClick: () => window.open(link.url, "_blank", "noopener,noreferrer"),
|
|
7772
|
+
style: {
|
|
7773
|
+
background: colors.border,
|
|
7774
|
+
color: "white",
|
|
7775
|
+
border: "none",
|
|
7776
|
+
borderRadius: "5px",
|
|
7777
|
+
padding: "4px 10px",
|
|
7778
|
+
fontSize: "11px",
|
|
7779
|
+
fontWeight: "500",
|
|
7780
|
+
cursor: "pointer",
|
|
7781
|
+
transition: "all 0.2s ease",
|
|
7782
|
+
flexShrink: 0
|
|
7783
|
+
},
|
|
7784
|
+
onMouseEnter: (e) => {
|
|
7785
|
+
e.currentTarget.style.opacity = "0.9";
|
|
7786
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
7787
|
+
},
|
|
7788
|
+
onMouseLeave: (e) => {
|
|
7789
|
+
e.currentTarget.style.opacity = "1";
|
|
7790
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
7791
|
+
},
|
|
7792
|
+
children: link.text
|
|
7793
|
+
}),
|
|
7794
|
+
details && /* @__PURE__ */ jsx("button", {
|
|
7795
|
+
onClick: () => setDetailsExpanded(!detailsExpanded),
|
|
7796
|
+
style: {
|
|
7797
|
+
background: "transparent",
|
|
7798
|
+
border: `1px solid ${colors.border}`,
|
|
7799
|
+
borderRadius: "5px",
|
|
7800
|
+
padding: "4px 10px",
|
|
7801
|
+
fontSize: "11px",
|
|
7802
|
+
fontWeight: "500",
|
|
7803
|
+
cursor: "pointer",
|
|
7804
|
+
color: colors.text,
|
|
7805
|
+
flexShrink: 0,
|
|
7806
|
+
transition: "all 0.2s ease"
|
|
7807
|
+
},
|
|
7808
|
+
onMouseEnter: (e) => {
|
|
7809
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
7810
|
+
},
|
|
7811
|
+
onMouseLeave: (e) => {
|
|
7812
|
+
e.currentTarget.style.background = "transparent";
|
|
7813
|
+
},
|
|
7814
|
+
children: detailsExpanded ? "Hide Details" : "Show Details"
|
|
7815
|
+
})
|
|
7816
|
+
]
|
|
7817
|
+
})]
|
|
7818
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
7819
|
+
onClick: onDismiss,
|
|
7820
|
+
style: {
|
|
7821
|
+
background: "transparent",
|
|
7822
|
+
border: "none",
|
|
7823
|
+
color: colors.text,
|
|
7824
|
+
cursor: "pointer",
|
|
7825
|
+
padding: "2px",
|
|
7826
|
+
borderRadius: "3px",
|
|
7827
|
+
fontSize: "14px",
|
|
7828
|
+
lineHeight: "1",
|
|
7829
|
+
opacity: .6,
|
|
7830
|
+
transition: "all 0.2s ease",
|
|
7831
|
+
flexShrink: 0
|
|
7832
|
+
},
|
|
7833
|
+
title: "Dismiss",
|
|
7834
|
+
onMouseEnter: (e) => {
|
|
7835
|
+
e.currentTarget.style.opacity = "1";
|
|
7836
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
7837
|
+
},
|
|
7838
|
+
onMouseLeave: (e) => {
|
|
7839
|
+
e.currentTarget.style.opacity = "0.6";
|
|
7840
|
+
e.currentTarget.style.background = "transparent";
|
|
7841
|
+
},
|
|
7842
|
+
children: "x"
|
|
7843
|
+
})]
|
|
7844
|
+
}), detailsExpanded && details && /* @__PURE__ */ jsxs("div", {
|
|
7845
|
+
style: {
|
|
7846
|
+
marginTop: "10px",
|
|
7847
|
+
padding: "10px",
|
|
7848
|
+
background: "rgba(0, 0, 0, 0.04)",
|
|
7849
|
+
borderRadius: "6px",
|
|
7850
|
+
fontSize: "11px",
|
|
7851
|
+
fontFamily: "monospace",
|
|
7852
|
+
color: colors.text,
|
|
7853
|
+
lineHeight: "1.5",
|
|
7854
|
+
maxHeight: "200px",
|
|
7855
|
+
overflowY: "auto",
|
|
7856
|
+
whiteSpace: "pre-wrap",
|
|
7857
|
+
wordBreak: "break-all"
|
|
7858
|
+
},
|
|
7859
|
+
children: [
|
|
7860
|
+
details.code && /* @__PURE__ */ jsxs("div", { children: [
|
|
7861
|
+
/* @__PURE__ */ jsx("strong", { children: "Code:" }),
|
|
7862
|
+
" ",
|
|
7863
|
+
details.code
|
|
7864
|
+
] }),
|
|
7865
|
+
details.originalMessage && /* @__PURE__ */ jsxs("div", {
|
|
7866
|
+
style: { marginTop: "4px" },
|
|
7867
|
+
children: [
|
|
7868
|
+
/* @__PURE__ */ jsx("strong", { children: "Message:" }),
|
|
7869
|
+
" ",
|
|
7870
|
+
details.originalMessage
|
|
7871
|
+
]
|
|
7872
|
+
}),
|
|
7873
|
+
details.context && Object.keys(details.context).length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
7874
|
+
style: { marginTop: "4px" },
|
|
7875
|
+
children: [
|
|
7876
|
+
/* @__PURE__ */ jsx("strong", { children: "Context:" }),
|
|
7877
|
+
" ",
|
|
7878
|
+
JSON.stringify(details.context, null, 2)
|
|
7879
|
+
]
|
|
7880
|
+
}),
|
|
7881
|
+
details.stack && /* @__PURE__ */ jsxs("div", {
|
|
7882
|
+
style: {
|
|
7883
|
+
marginTop: "4px",
|
|
7884
|
+
opacity: .7
|
|
7885
|
+
},
|
|
7886
|
+
children: [
|
|
7887
|
+
/* @__PURE__ */ jsx("strong", { children: "Stack:" }),
|
|
7888
|
+
"\n",
|
|
7889
|
+
details.stack
|
|
7890
|
+
]
|
|
7891
|
+
})
|
|
7892
|
+
]
|
|
7893
|
+
})]
|
|
7894
|
+
});
|
|
7895
|
+
}
|
|
5909
7896
|
function ToastProvider({ enabled, children }) {
|
|
5910
7897
|
const [toasts, setToasts] = useState([]);
|
|
5911
7898
|
const [bannerError, setBannerErrorState] = useState(null);
|
|
@@ -5943,156 +7930,10 @@ function ToastProvider({ enabled, children }) {
|
|
|
5943
7930
|
};
|
|
5944
7931
|
return /* @__PURE__ */ jsxs(ToastContext.Provider, {
|
|
5945
7932
|
value,
|
|
5946
|
-
children: [bannerError && (
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
position: "fixed",
|
|
5951
|
-
bottom: "20px",
|
|
5952
|
-
left: "50%",
|
|
5953
|
-
transform: "translateX(-50%)",
|
|
5954
|
-
zIndex: 9999,
|
|
5955
|
-
backgroundColor: colors.background,
|
|
5956
|
-
border: `1px solid ${colors.border}`,
|
|
5957
|
-
borderLeft: `4px solid ${colors.border}`,
|
|
5958
|
-
borderRadius: "8px",
|
|
5959
|
-
padding: "12px 16px",
|
|
5960
|
-
fontSize: "13px",
|
|
5961
|
-
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
5962
|
-
backdropFilter: "blur(8px)",
|
|
5963
|
-
maxWidth: "min(90vw, 700px)",
|
|
5964
|
-
width: "100%",
|
|
5965
|
-
boxSizing: "border-box",
|
|
5966
|
-
overflow: "hidden"
|
|
5967
|
-
},
|
|
5968
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
5969
|
-
style: {
|
|
5970
|
-
display: "flex",
|
|
5971
|
-
justifyContent: "space-between",
|
|
5972
|
-
alignItems: "center",
|
|
5973
|
-
gap: "10px"
|
|
5974
|
-
},
|
|
5975
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
5976
|
-
style: {
|
|
5977
|
-
display: "flex",
|
|
5978
|
-
alignItems: "center",
|
|
5979
|
-
gap: "8px",
|
|
5980
|
-
flex: 1,
|
|
5981
|
-
minWidth: 0
|
|
5982
|
-
},
|
|
5983
|
-
children: [/* @__PURE__ */ jsx("div", { style: {
|
|
5984
|
-
width: "12px",
|
|
5985
|
-
height: "12px",
|
|
5986
|
-
borderRadius: "50%",
|
|
5987
|
-
backgroundColor: colors.border,
|
|
5988
|
-
flexShrink: 0
|
|
5989
|
-
} }), /* @__PURE__ */ jsxs("div", {
|
|
5990
|
-
style: {
|
|
5991
|
-
display: "flex",
|
|
5992
|
-
alignItems: "center",
|
|
5993
|
-
gap: "10px",
|
|
5994
|
-
flex: 1,
|
|
5995
|
-
minWidth: 0
|
|
5996
|
-
},
|
|
5997
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
5998
|
-
style: {
|
|
5999
|
-
color: colors.text,
|
|
6000
|
-
lineHeight: "1.4",
|
|
6001
|
-
fontWeight: "400",
|
|
6002
|
-
fontSize: "13px",
|
|
6003
|
-
flex: 1,
|
|
6004
|
-
wordBreak: "break-all",
|
|
6005
|
-
overflowWrap: "break-word",
|
|
6006
|
-
maxWidth: "550px",
|
|
6007
|
-
overflow: "hidden",
|
|
6008
|
-
display: "-webkit-box",
|
|
6009
|
-
WebkitLineClamp: 10,
|
|
6010
|
-
WebkitBoxOrient: "vertical"
|
|
6011
|
-
},
|
|
6012
|
-
children: (() => {
|
|
6013
|
-
let message = bannerError.message;
|
|
6014
|
-
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
|
|
6015
|
-
if (jsonMatch) return jsonMatch[1];
|
|
6016
|
-
message = message.split(" - ")[0];
|
|
6017
|
-
message = message.split(": Error code")[0];
|
|
6018
|
-
message = message.replace(/:\s*\d{3}$/, "");
|
|
6019
|
-
message = message.replace(/See more:.*$/g, "");
|
|
6020
|
-
message = message.trim();
|
|
6021
|
-
return message || "Configuration error occurred.";
|
|
6022
|
-
})()
|
|
6023
|
-
}), (() => {
|
|
6024
|
-
const message = bannerError.message;
|
|
6025
|
-
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
6026
|
-
const plainUrlRegex = /(https?:\/\/[^\s)]+)/g;
|
|
6027
|
-
let url = null;
|
|
6028
|
-
let buttonText = "See More";
|
|
6029
|
-
const markdownMatch = markdownLinkRegex.exec(message);
|
|
6030
|
-
if (markdownMatch) {
|
|
6031
|
-
url = markdownMatch[2];
|
|
6032
|
-
buttonText = "See More";
|
|
6033
|
-
} else {
|
|
6034
|
-
const urlMatch = plainUrlRegex.exec(message);
|
|
6035
|
-
if (urlMatch) {
|
|
6036
|
-
url = urlMatch[0].replace(/[.,;:'"]*$/, "");
|
|
6037
|
-
buttonText = "See More";
|
|
6038
|
-
}
|
|
6039
|
-
}
|
|
6040
|
-
if (!url) return null;
|
|
6041
|
-
return /* @__PURE__ */ jsx("button", {
|
|
6042
|
-
onClick: () => window.open(url, "_blank", "noopener,noreferrer"),
|
|
6043
|
-
style: {
|
|
6044
|
-
background: colors.border,
|
|
6045
|
-
color: "white",
|
|
6046
|
-
border: "none",
|
|
6047
|
-
borderRadius: "5px",
|
|
6048
|
-
padding: "4px 10px",
|
|
6049
|
-
fontSize: "11px",
|
|
6050
|
-
fontWeight: "500",
|
|
6051
|
-
cursor: "pointer",
|
|
6052
|
-
transition: "all 0.2s ease",
|
|
6053
|
-
flexShrink: 0
|
|
6054
|
-
},
|
|
6055
|
-
onMouseEnter: (e) => {
|
|
6056
|
-
e.currentTarget.style.opacity = "0.9";
|
|
6057
|
-
e.currentTarget.style.transform = "translateY(-1px)";
|
|
6058
|
-
},
|
|
6059
|
-
onMouseLeave: (e) => {
|
|
6060
|
-
e.currentTarget.style.opacity = "1";
|
|
6061
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
6062
|
-
},
|
|
6063
|
-
children: buttonText
|
|
6064
|
-
});
|
|
6065
|
-
})()]
|
|
6066
|
-
})]
|
|
6067
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
6068
|
-
onClick: () => setBannerError(null),
|
|
6069
|
-
style: {
|
|
6070
|
-
background: "transparent",
|
|
6071
|
-
border: "none",
|
|
6072
|
-
color: colors.text,
|
|
6073
|
-
cursor: "pointer",
|
|
6074
|
-
padding: "2px",
|
|
6075
|
-
borderRadius: "3px",
|
|
6076
|
-
fontSize: "14px",
|
|
6077
|
-
lineHeight: "1",
|
|
6078
|
-
opacity: .6,
|
|
6079
|
-
transition: "all 0.2s ease",
|
|
6080
|
-
flexShrink: 0
|
|
6081
|
-
},
|
|
6082
|
-
title: "Dismiss",
|
|
6083
|
-
onMouseEnter: (e) => {
|
|
6084
|
-
e.currentTarget.style.opacity = "1";
|
|
6085
|
-
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
6086
|
-
},
|
|
6087
|
-
onMouseLeave: (e) => {
|
|
6088
|
-
e.currentTarget.style.opacity = "0.6";
|
|
6089
|
-
e.currentTarget.style.background = "transparent";
|
|
6090
|
-
},
|
|
6091
|
-
children: "×"
|
|
6092
|
-
})]
|
|
6093
|
-
})
|
|
6094
|
-
});
|
|
6095
|
-
})(), children]
|
|
7933
|
+
children: [bannerError && /* @__PURE__ */ jsx(BannerErrorDisplay, {
|
|
7934
|
+
bannerError,
|
|
7935
|
+
onDismiss: () => setBannerError(null)
|
|
7936
|
+
}), children]
|
|
6096
7937
|
});
|
|
6097
7938
|
}
|
|
6098
7939
|
|
|
@@ -7099,12 +8940,21 @@ function CopilotListeners() {
|
|
|
7099
8940
|
const { agent } = useAgent({ agentId: resolvedAgentId });
|
|
7100
8941
|
usePredictStateSubscription(agent);
|
|
7101
8942
|
useEffect(() => {
|
|
7102
|
-
const subscription = copilotkit.subscribe({ onError: ({ error }) => {
|
|
7103
|
-
|
|
8943
|
+
const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
|
|
8944
|
+
if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
|
|
8945
|
+
if (process.env.NODE_ENV === "development") console.error("[CopilotKit] Agent error:", error.message, "\n Code:", code, "\n Context:", context, "\n Stack:", error.stack);
|
|
8946
|
+
const ckError = new CopilotKitLowLevelError({
|
|
7104
8947
|
error,
|
|
7105
8948
|
message: error.message,
|
|
7106
8949
|
url: typeof window !== "undefined" ? window.location.href : ""
|
|
7107
|
-
})
|
|
8950
|
+
});
|
|
8951
|
+
ckError.details = {
|
|
8952
|
+
code,
|
|
8953
|
+
context,
|
|
8954
|
+
stack: error.stack,
|
|
8955
|
+
originalMessage: error.message
|
|
8956
|
+
};
|
|
8957
|
+
setBannerError(ckError);
|
|
7108
8958
|
} });
|
|
7109
8959
|
return () => {
|
|
7110
8960
|
subscription.unsubscribe();
|
|
@@ -7622,5 +9472,5 @@ function validateProps(props) {
|
|
|
7622
9472
|
}
|
|
7623
9473
|
|
|
7624
9474
|
//#endregion
|
|
7625
|
-
export {
|
|
7626
|
-
//# sourceMappingURL=copilotkit-
|
|
9475
|
+
export { useCopilotKit as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useComponent as G, useHumanInTheLoop as H, CopilotChatToolCallsView as I, useRenderCustomMessages as J, useFrontendTool as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, CopilotKitProvider as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useDefaultRenderTool as U, useSuggestions as V, useRenderTool as W, useAgent as X, UseAgentUpdate as Y, useRenderToolCall as Z, WildcardToolCallRender as _, ThreadsProvider as a, useSandboxFunctions as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, MCPAppsActivityType as ct, shouldShowDevConsole as d, AudioRecorderError as dt, CopilotKitCoreReact as et, useToast as f, CopilotChatAudioRecorder as ft, useCopilotContext as g, CopilotContext as h, ThreadsContext as i, SandboxFunctionsContext as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, CopilotKitInspector as lt, useCopilotMessagesContext as m, useCopilotChatConfiguration as mt, defaultCopilotContextCategories as n, defineToolCallRenderer as nt, useThreads as o, MCPAppsActivityContentSchema as ot, CopilotMessagesContext as p, CopilotChatConfigurationProvider as pt, useRenderActivityMessage as q, CoAgentStateRenderBridge as r, createA2UIMessageRenderer as rt, CoAgentStateRendersContext as s, MCPAppsActivityRenderer as st, CopilotKit as t, useAgentContext as tt, useAsyncCallback as u, CopilotChatInput_default as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
|
|
9476
|
+
//# sourceMappingURL=copilotkit-BY5S1-0P.mjs.map
|