@copilotkit/react-core 1.55.0-next.8 → 1.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -5
- package/dist/{copilotkit-DNYSFuz5.mjs → copilotkit-BY5S1-0P.mjs} +2772 -858
- package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
- package/dist/{copilotkit-Dy5w3qEV.d.mts → copilotkit-BuhSUZHb.d.mts} +230 -17
- package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
- package/dist/{copilotkit-B3Mb1yVE.cjs → copilotkit-Bz5-ImDl.cjs} +2776 -832
- package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
- package/dist/{copilotkit-DBzgOMby.d.cts → copilotkit-dwDWYpya.d.cts} +230 -17
- package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +9 -4
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1624 -396
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +13 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +3 -3
- package/dist/v2/index.d.mts +3 -3
- package/dist/v2/index.mjs +3 -2
- package/dist/v2/index.umd.js +2746 -790
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +62 -54
- package/scripts/scope-preflight.mjs +1 -2
- package/src/components/CopilotListeners.tsx +41 -8
- package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +92 -0
- package/src/components/copilot-provider/copilotkit-props.tsx +4 -2
- package/src/components/copilot-provider/copilotkit.tsx +3 -3
- package/src/components/toast/toast-provider.tsx +269 -194
- package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +27 -16
- package/src/hooks/use-copilot-chat_internal.ts +15 -4
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
- package/src/v2/__tests__/utils/test-helpers.tsx +107 -7
- package/src/v2/a2ui/A2UICatalogContext.tsx +79 -0
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +125 -37
- package/src/v2/a2ui/A2UIToolCallRenderer.tsx +290 -0
- package/src/v2/components/CopilotKitInspector.tsx +2 -0
- package/src/v2/components/OpenGenerativeUIRenderer.tsx +598 -0
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +665 -0
- package/src/v2/components/chat/CopilotChat.tsx +197 -52
- package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +17 -2
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +481 -0
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +139 -0
- package/src/v2/components/chat/CopilotChatInput.tsx +146 -77
- package/src/v2/components/chat/CopilotChatMessageView.tsx +260 -151
- package/src/v2/components/chat/CopilotChatSuggestionView.tsx +1 -0
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +54 -0
- package/src/v2/components/chat/CopilotChatView.tsx +179 -66
- package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +168 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +63 -2
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +544 -1
- package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +268 -0
- package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +249 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +60 -3
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +138 -0
- package/src/v2/components/chat/index.ts +9 -0
- package/src/v2/components/chat/scroll-element-context.ts +13 -0
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +8 -0
- package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +327 -0
- package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +1003 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +13 -2
- package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +23 -4
- package/src/v2/hooks/__tests__/use-threads.test.tsx +54 -0
- package/src/v2/hooks/index.ts +5 -0
- package/src/v2/hooks/use-agent.tsx +220 -15
- package/src/v2/hooks/use-attachments.tsx +269 -0
- package/src/v2/hooks/use-frontend-tool.tsx +5 -2
- package/src/v2/hooks/use-render-activity-message.tsx +9 -2
- package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
- package/src/v2/hooks/use-threads.tsx +35 -15
- package/src/v2/index.ts +5 -1
- package/src/v2/lib/__tests__/processPartialHtml.test.ts +112 -0
- package/src/v2/lib/__tests__/slots.test.ts +56 -0
- package/src/v2/lib/processPartialHtml.ts +45 -0
- package/src/v2/lib/slots.tsx +42 -1
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +9 -3
- package/src/v2/providers/CopilotKitProvider.tsx +268 -32
- package/src/v2/providers/SandboxFunctionsContext.ts +10 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +198 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +71 -0
- package/src/v2/providers/index.ts +7 -0
- package/src/v2/styles/globals.css +2 -1
- package/src/v2/types/index.ts +1 -0
- package/src/v2/types/sandbox-function.ts +11 -0
- package/dist/copilotkit-B3Mb1yVE.cjs.map +0 -1
- package/dist/copilotkit-DBzgOMby.d.cts.map +0 -1
- package/dist/copilotkit-DNYSFuz5.mjs.map +0 -1
- package/dist/copilotkit-Dy5w3qEV.d.mts.map +0 -1
- package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
|
@@ -44,19 +44,120 @@ let streamdown = require("streamdown");
|
|
|
44
44
|
let zod = require("zod");
|
|
45
45
|
let _lit_labs_react = require("@lit-labs/react");
|
|
46
46
|
let _copilotkit_a2ui_renderer = require("@copilotkit/a2ui-renderer");
|
|
47
|
-
let
|
|
48
|
-
let
|
|
47
|
+
let zod_to_json_schema = require("zod-to-json-schema");
|
|
48
|
+
let _tanstack_react_virtual = require("@tanstack/react-virtual");
|
|
49
49
|
let react_dom = require("react-dom");
|
|
50
|
+
let use_stick_to_bottom = require("use-stick-to-bottom");
|
|
50
51
|
let react_markdown = require("react-markdown");
|
|
51
52
|
react_markdown = __toESM(react_markdown);
|
|
52
53
|
|
|
54
|
+
//#region src/v2/lib/slots.tsx
|
|
55
|
+
/**
|
|
56
|
+
* Shallow equality comparison for objects.
|
|
57
|
+
*/
|
|
58
|
+
function shallowEqual(obj1, obj2) {
|
|
59
|
+
const keys1 = Object.keys(obj1);
|
|
60
|
+
const keys2 = Object.keys(obj2);
|
|
61
|
+
if (keys1.length !== keys2.length) return false;
|
|
62
|
+
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
|
|
67
|
+
* class instances, and other exotic objects that happen to have typeof "object".
|
|
68
|
+
*/
|
|
69
|
+
function isPlainObject(obj) {
|
|
70
|
+
return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns the same reference as long as the value is shallowly equal to the
|
|
74
|
+
* previous render's value.
|
|
75
|
+
*
|
|
76
|
+
* - Identical references bail out immediately (O(1)).
|
|
77
|
+
* - Plain objects ({}) are shallow-compared key-by-key.
|
|
78
|
+
* - Arrays, Dates, class instances, functions, and primitives are compared by
|
|
79
|
+
* reference only — shallowEqual is never called on non-plain objects, which
|
|
80
|
+
* avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
|
|
81
|
+
*
|
|
82
|
+
* Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
|
|
83
|
+
* equality check isn't defeated by a new object reference on every render.
|
|
84
|
+
*/
|
|
85
|
+
function useShallowStableRef(value) {
|
|
86
|
+
const ref = (0, react.useRef)(value);
|
|
87
|
+
if (ref.current === value) return ref.current;
|
|
88
|
+
if (isPlainObject(ref.current) && isPlainObject(value)) {
|
|
89
|
+
if (shallowEqual(ref.current, value)) return ref.current;
|
|
90
|
+
}
|
|
91
|
+
ref.current = value;
|
|
92
|
+
return ref.current;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
|
|
96
|
+
*/
|
|
97
|
+
function isReactComponentType(value) {
|
|
98
|
+
if (typeof value === "function") return true;
|
|
99
|
+
if (value && typeof value === "object" && "$$typeof" in value && !react.default.isValidElement(value)) return true;
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Internal function to render a slot value as a React element (non-memoized).
|
|
104
|
+
*/
|
|
105
|
+
function renderSlotElement(slot, DefaultComponent, props) {
|
|
106
|
+
if (typeof slot === "string") {
|
|
107
|
+
const existingClassName = props.className;
|
|
108
|
+
return react.default.createElement(DefaultComponent, {
|
|
109
|
+
...props,
|
|
110
|
+
className: (0, tailwind_merge.twMerge)(existingClassName, slot)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (isReactComponentType(slot)) return react.default.createElement(slot, props);
|
|
114
|
+
if (slot && typeof slot === "object" && !react.default.isValidElement(slot)) return react.default.createElement(DefaultComponent, {
|
|
115
|
+
...props,
|
|
116
|
+
...slot
|
|
117
|
+
});
|
|
118
|
+
return react.default.createElement(DefaultComponent, props);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Internal memoized wrapper component for renderSlot.
|
|
122
|
+
* Uses forwardRef to support ref forwarding.
|
|
123
|
+
*/
|
|
124
|
+
const MemoizedSlotWrapper = react.default.memo(react.default.forwardRef(function MemoizedSlotWrapper(props, ref) {
|
|
125
|
+
const { $slot, $component, ...rest } = props;
|
|
126
|
+
return renderSlotElement($slot, $component, ref !== null ? {
|
|
127
|
+
...rest,
|
|
128
|
+
ref
|
|
129
|
+
} : rest);
|
|
130
|
+
}), (prev, next) => {
|
|
131
|
+
if (prev.$slot !== next.$slot) return false;
|
|
132
|
+
if (prev.$component !== next.$component) return false;
|
|
133
|
+
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
|
|
134
|
+
const { $slot: _ns, $component: _nc, ...nextRest } = next;
|
|
135
|
+
return shallowEqual(prevRest, nextRest);
|
|
136
|
+
});
|
|
137
|
+
/**
|
|
138
|
+
* Renders a slot value as a memoized React element.
|
|
139
|
+
* Automatically prevents unnecessary re-renders using shallow prop comparison.
|
|
140
|
+
* Supports ref forwarding.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
|
|
144
|
+
*/
|
|
145
|
+
function renderSlot(slot, DefaultComponent, props) {
|
|
146
|
+
return react.default.createElement(MemoizedSlotWrapper, {
|
|
147
|
+
...props,
|
|
148
|
+
$slot: slot,
|
|
149
|
+
$component: DefaultComponent
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
53
154
|
//#region src/v2/providers/CopilotChatConfigurationProvider.tsx
|
|
54
155
|
const CopilotChatDefaultLabels = {
|
|
55
156
|
chatInputPlaceholder: "Type a message...",
|
|
56
157
|
chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
|
|
57
158
|
chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
|
|
58
159
|
chatInputToolbarFinishTranscribeButtonLabel: "Finish",
|
|
59
|
-
chatInputToolbarAddButtonLabel: "Add
|
|
160
|
+
chatInputToolbarAddButtonLabel: "Add attachments",
|
|
60
161
|
chatInputToolbarToolsButtonLabel: "Tools",
|
|
61
162
|
assistantMessageToolbarCopyCodeLabel: "Copy",
|
|
62
163
|
assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
|
|
@@ -76,11 +177,12 @@ const CopilotChatDefaultLabels = {
|
|
|
76
177
|
const CopilotChatConfiguration = (0, react.createContext)(null);
|
|
77
178
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
|
|
78
179
|
const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
|
|
180
|
+
const stableLabels = useShallowStableRef(labels);
|
|
79
181
|
const mergedLabels = (0, react.useMemo)(() => ({
|
|
80
182
|
...CopilotChatDefaultLabels,
|
|
81
183
|
...parentConfig?.labels ?? {},
|
|
82
|
-
...
|
|
83
|
-
}), [
|
|
184
|
+
...stableLabels ?? {}
|
|
185
|
+
}), [stableLabels, parentConfig?.labels]);
|
|
84
186
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
85
187
|
const resolvedThreadId = (0, react.useMemo)(() => {
|
|
86
188
|
if (threadId) return threadId;
|
|
@@ -129,9 +231,9 @@ const useCopilotChatConfiguration = () => {
|
|
|
129
231
|
|
|
130
232
|
//#endregion
|
|
131
233
|
//#region src/v2/lib/utils.ts
|
|
132
|
-
const twMerge$
|
|
234
|
+
const twMerge$7 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
|
|
133
235
|
function cn(...inputs) {
|
|
134
|
-
return twMerge$
|
|
236
|
+
return twMerge$7((0, clsx.clsx)(inputs));
|
|
135
237
|
}
|
|
136
238
|
|
|
137
239
|
//#endregion
|
|
@@ -503,82 +605,11 @@ const CopilotChatAudioRecorder = (0, react.forwardRef)((props, ref) => {
|
|
|
503
605
|
});
|
|
504
606
|
CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
|
|
505
607
|
|
|
506
|
-
//#endregion
|
|
507
|
-
//#region src/v2/lib/slots.tsx
|
|
508
|
-
/**
|
|
509
|
-
* Shallow equality comparison for objects.
|
|
510
|
-
*/
|
|
511
|
-
function shallowEqual(obj1, obj2) {
|
|
512
|
-
const keys1 = Object.keys(obj1);
|
|
513
|
-
const keys2 = Object.keys(obj2);
|
|
514
|
-
if (keys1.length !== keys2.length) return false;
|
|
515
|
-
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
|
|
516
|
-
return true;
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
|
|
520
|
-
*/
|
|
521
|
-
function isReactComponentType(value) {
|
|
522
|
-
if (typeof value === "function") return true;
|
|
523
|
-
if (value && typeof value === "object" && "$$typeof" in value && !react.default.isValidElement(value)) return true;
|
|
524
|
-
return false;
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* Internal function to render a slot value as a React element (non-memoized).
|
|
528
|
-
*/
|
|
529
|
-
function renderSlotElement(slot, DefaultComponent, props) {
|
|
530
|
-
if (typeof slot === "string") {
|
|
531
|
-
const existingClassName = props.className;
|
|
532
|
-
return react.default.createElement(DefaultComponent, {
|
|
533
|
-
...props,
|
|
534
|
-
className: (0, tailwind_merge.twMerge)(existingClassName, slot)
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
if (isReactComponentType(slot)) return react.default.createElement(slot, props);
|
|
538
|
-
if (slot && typeof slot === "object" && !react.default.isValidElement(slot)) return react.default.createElement(DefaultComponent, {
|
|
539
|
-
...props,
|
|
540
|
-
...slot
|
|
541
|
-
});
|
|
542
|
-
return react.default.createElement(DefaultComponent, props);
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Internal memoized wrapper component for renderSlot.
|
|
546
|
-
* Uses forwardRef to support ref forwarding.
|
|
547
|
-
*/
|
|
548
|
-
const MemoizedSlotWrapper = react.default.memo(react.default.forwardRef(function MemoizedSlotWrapper(props, ref) {
|
|
549
|
-
const { $slot, $component, ...rest } = props;
|
|
550
|
-
return renderSlotElement($slot, $component, ref !== null ? {
|
|
551
|
-
...rest,
|
|
552
|
-
ref
|
|
553
|
-
} : rest);
|
|
554
|
-
}), (prev, next) => {
|
|
555
|
-
if (prev.$slot !== next.$slot) return false;
|
|
556
|
-
if (prev.$component !== next.$component) return false;
|
|
557
|
-
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
|
|
558
|
-
const { $slot: _ns, $component: _nc, ...nextRest } = next;
|
|
559
|
-
return shallowEqual(prevRest, nextRest);
|
|
560
|
-
});
|
|
561
|
-
/**
|
|
562
|
-
* Renders a slot value as a memoized React element.
|
|
563
|
-
* Automatically prevents unnecessary re-renders using shallow prop comparison.
|
|
564
|
-
* Supports ref forwarding.
|
|
565
|
-
*
|
|
566
|
-
* @example
|
|
567
|
-
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
|
|
568
|
-
*/
|
|
569
|
-
function renderSlot(slot, DefaultComponent, props) {
|
|
570
|
-
return react.default.createElement(MemoizedSlotWrapper, {
|
|
571
|
-
...props,
|
|
572
|
-
$slot: slot,
|
|
573
|
-
$component: DefaultComponent
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
|
|
577
608
|
//#endregion
|
|
578
609
|
//#region src/v2/components/chat/CopilotChatInput.tsx
|
|
579
610
|
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
|
|
580
611
|
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
|
|
581
|
-
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus =
|
|
612
|
+
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 }) {
|
|
582
613
|
const isControlled = value !== void 0;
|
|
583
614
|
const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
|
|
584
615
|
(0, react.useEffect)(() => {
|
|
@@ -607,6 +638,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
607
638
|
paddingLeft: 0,
|
|
608
639
|
paddingRight: 0
|
|
609
640
|
});
|
|
641
|
+
const containerCacheRef = (0, react.useRef)(null);
|
|
610
642
|
const commandItems = (0, react.useMemo)(() => {
|
|
611
643
|
const entries = [];
|
|
612
644
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -651,7 +683,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
651
683
|
previousModalStateRef.current = config?.isModalOpen;
|
|
652
684
|
return;
|
|
653
685
|
}
|
|
654
|
-
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus();
|
|
686
|
+
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
|
|
655
687
|
previousModalStateRef.current = config?.isModalOpen;
|
|
656
688
|
}, [config?.isModalOpen, autoFocus]);
|
|
657
689
|
(0, react.useEffect)(() => {
|
|
@@ -894,6 +926,25 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
894
926
|
return nextLayout;
|
|
895
927
|
});
|
|
896
928
|
}, []);
|
|
929
|
+
const updateContainerCache = (0, react.useCallback)(() => {
|
|
930
|
+
const grid = gridRef.current;
|
|
931
|
+
const addContainer = addButtonContainerRef.current;
|
|
932
|
+
const actionsContainer = actionsContainerRef.current;
|
|
933
|
+
if (!grid || !addContainer || !actionsContainer) return null;
|
|
934
|
+
const gridStyles = window.getComputedStyle(grid);
|
|
935
|
+
const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
|
|
936
|
+
const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
|
|
937
|
+
const columnGap = parseFloat(gridStyles.columnGap) || 0;
|
|
938
|
+
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
939
|
+
if (gridAvailableWidth <= 0) return null;
|
|
940
|
+
const addWidth = addContainer.getBoundingClientRect().width;
|
|
941
|
+
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
942
|
+
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
|
|
943
|
+
if (compactWidth <= 0) return null;
|
|
944
|
+
const result = { compactWidth };
|
|
945
|
+
containerCacheRef.current = result;
|
|
946
|
+
return result;
|
|
947
|
+
}, []);
|
|
897
948
|
const evaluateLayout = (0, react.useCallback)(() => {
|
|
898
949
|
if (mode !== "input") {
|
|
899
950
|
updateLayout("compact");
|
|
@@ -919,31 +970,31 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
919
970
|
const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
|
|
920
971
|
let shouldExpand = hasExplicitBreak || renderedMultiline;
|
|
921
972
|
if (!shouldExpand) {
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
927
|
-
if (gridAvailableWidth > 0) {
|
|
928
|
-
const addWidth = addContainer.getBoundingClientRect().width;
|
|
929
|
-
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
930
|
-
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
|
|
931
|
-
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
|
|
932
|
-
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
|
|
933
|
-
const context = canvas.getContext("2d");
|
|
934
|
-
if (context) {
|
|
973
|
+
const cache = containerCacheRef.current ?? updateContainerCache();
|
|
974
|
+
if (cache && cache.compactWidth > 0) {
|
|
975
|
+
const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
|
|
976
|
+
if (compactInnerWidth > 0) {
|
|
935
977
|
const textareaStyles = window.getComputedStyle(textarea);
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
let longestWidth = 0;
|
|
941
|
-
for (const line of lines) {
|
|
942
|
-
const metrics = context.measureText(line || " ");
|
|
943
|
-
if (metrics.width > longestWidth) longestWidth = metrics.width;
|
|
944
|
-
}
|
|
945
|
-
if (longestWidth > compactInnerWidth) shouldExpand = true;
|
|
978
|
+
let font = textareaStyles.font;
|
|
979
|
+
if (!font) {
|
|
980
|
+
const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
|
|
981
|
+
if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
|
|
946
982
|
}
|
|
983
|
+
if (font?.trim()) {
|
|
984
|
+
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
|
|
985
|
+
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
|
|
986
|
+
const context = canvas.getContext("2d");
|
|
987
|
+
if (context) {
|
|
988
|
+
context.font = font;
|
|
989
|
+
const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
|
|
990
|
+
let longestWidth = 0;
|
|
991
|
+
for (const line of lines) {
|
|
992
|
+
const metrics = context.measureText(line || " ");
|
|
993
|
+
if (metrics.width > longestWidth) longestWidth = metrics.width;
|
|
994
|
+
}
|
|
995
|
+
if (longestWidth > compactInnerWidth) shouldExpand = true;
|
|
996
|
+
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
|
|
997
|
+
} 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.");
|
|
947
998
|
}
|
|
948
999
|
}
|
|
949
1000
|
}
|
|
@@ -953,6 +1004,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
953
1004
|
ensureMeasurements,
|
|
954
1005
|
mode,
|
|
955
1006
|
resolvedValue,
|
|
1007
|
+
updateContainerCache,
|
|
956
1008
|
updateLayout
|
|
957
1009
|
]);
|
|
958
1010
|
(0, react.useLayoutEffect)(() => {
|
|
@@ -965,11 +1017,17 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
965
1017
|
const addContainer = addButtonContainerRef.current;
|
|
966
1018
|
const actionsContainer = actionsContainerRef.current;
|
|
967
1019
|
if (!textarea || !grid || !addContainer || !actionsContainer) return;
|
|
968
|
-
const
|
|
1020
|
+
const containerTargets = new Set([
|
|
1021
|
+
grid,
|
|
1022
|
+
addContainer,
|
|
1023
|
+
actionsContainer
|
|
1024
|
+
]);
|
|
1025
|
+
const scheduleEvaluation = (invalidateCache) => {
|
|
969
1026
|
if (ignoreResizeRef.current) {
|
|
970
1027
|
ignoreResizeRef.current = false;
|
|
971
1028
|
return;
|
|
972
1029
|
}
|
|
1030
|
+
if (invalidateCache) containerCacheRef.current = null;
|
|
973
1031
|
if (typeof window === "undefined") {
|
|
974
1032
|
evaluateLayout();
|
|
975
1033
|
return;
|
|
@@ -980,8 +1038,13 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
980
1038
|
evaluateLayout();
|
|
981
1039
|
});
|
|
982
1040
|
};
|
|
983
|
-
const observer = new ResizeObserver(() => {
|
|
984
|
-
|
|
1041
|
+
const observer = new ResizeObserver((entries) => {
|
|
1042
|
+
let shouldInvalidate = false;
|
|
1043
|
+
for (const entry of entries) if (containerTargets.has(entry.target)) {
|
|
1044
|
+
shouldInvalidate = true;
|
|
1045
|
+
break;
|
|
1046
|
+
}
|
|
1047
|
+
scheduleEvaluation(shouldInvalidate);
|
|
985
1048
|
});
|
|
986
1049
|
observer.observe(grid);
|
|
987
1050
|
observer.observe(addContainer);
|
|
@@ -1126,6 +1189,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1126
1189
|
});
|
|
1127
1190
|
_CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
|
|
1128
1191
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1192
|
+
const [mounted, setMounted] = (0, react.useState)(false);
|
|
1193
|
+
(0, react.useEffect)(() => setMounted(true), []);
|
|
1129
1194
|
const menuItems = (0, react.useMemo)(() => {
|
|
1130
1195
|
const items = [];
|
|
1131
1196
|
if (onAddFile) items.push({
|
|
@@ -1156,26 +1221,28 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1156
1221
|
}), []);
|
|
1157
1222
|
const hasMenuItems = menuItems.length > 0;
|
|
1158
1223
|
const isDisabled = disabled || !hasMenuItems;
|
|
1224
|
+
const button = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1225
|
+
type: "button",
|
|
1226
|
+
"data-testid": "copilot-add-menu-button",
|
|
1227
|
+
variant: "chatInputToolbarSecondary",
|
|
1228
|
+
size: "chatInputToolbarIcon",
|
|
1229
|
+
className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
|
|
1230
|
+
disabled: isDisabled,
|
|
1231
|
+
...props,
|
|
1232
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
|
|
1233
|
+
});
|
|
1234
|
+
if (!mounted) return button;
|
|
1159
1235
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
|
|
1160
1236
|
asChild: true,
|
|
1161
1237
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuTrigger, {
|
|
1162
1238
|
asChild: true,
|
|
1163
|
-
children:
|
|
1164
|
-
type: "button",
|
|
1165
|
-
"data-testid": "copilot-add-menu-button",
|
|
1166
|
-
variant: "chatInputToolbarSecondary",
|
|
1167
|
-
size: "chatInputToolbarIcon",
|
|
1168
|
-
className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
|
|
1169
|
-
disabled: isDisabled,
|
|
1170
|
-
...props,
|
|
1171
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
|
|
1172
|
-
})
|
|
1239
|
+
children: button
|
|
1173
1240
|
})
|
|
1174
1241
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
|
|
1175
1242
|
side: "bottom",
|
|
1176
1243
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1177
1244
|
className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
|
|
1178
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add
|
|
1245
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add attachments" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
1179
1246
|
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",
|
|
1180
1247
|
children: "/"
|
|
1181
1248
|
})]
|
|
@@ -1191,21 +1258,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1191
1258
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1192
1259
|
(0, react.useImperativeHandle)(ref, () => internalTextareaRef.current);
|
|
1193
1260
|
(0, react.useEffect)(() => {
|
|
1194
|
-
|
|
1195
|
-
if (!textarea) return;
|
|
1196
|
-
const handleFocus = () => {
|
|
1197
|
-
setTimeout(() => {
|
|
1198
|
-
textarea.scrollIntoView({
|
|
1199
|
-
behavior: "smooth",
|
|
1200
|
-
block: "nearest"
|
|
1201
|
-
});
|
|
1202
|
-
}, 300);
|
|
1203
|
-
};
|
|
1204
|
-
textarea.addEventListener("focus", handleFocus);
|
|
1205
|
-
return () => textarea.removeEventListener("focus", handleFocus);
|
|
1206
|
-
}, []);
|
|
1207
|
-
(0, react.useEffect)(() => {
|
|
1208
|
-
if (autoFocus) internalTextareaRef.current?.focus();
|
|
1261
|
+
if (autoFocus) internalTextareaRef.current?.focus({ preventScroll: true });
|
|
1209
1262
|
}, [autoFocus]);
|
|
1210
1263
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
1211
1264
|
ref: internalTextareaRef,
|
|
@@ -1913,125 +1966,981 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
|
|
|
1913
1966
|
};
|
|
1914
1967
|
|
|
1915
1968
|
//#endregion
|
|
1916
|
-
//#region src/v2/
|
|
1917
|
-
|
|
1918
|
-
function
|
|
1919
|
-
|
|
1920
|
-
(0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
|
|
1921
|
-
(0, _copilotkit_a2ui_renderer.injectStyles)();
|
|
1922
|
-
initialized = true;
|
|
1923
|
-
}
|
|
1969
|
+
//#region src/v2/providers/SandboxFunctionsContext.ts
|
|
1970
|
+
const SandboxFunctionsContext = (0, react.createContext)([]);
|
|
1971
|
+
function useSandboxFunctions() {
|
|
1972
|
+
return (0, react.useContext)(SandboxFunctionsContext);
|
|
1924
1973
|
}
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
(0, react.useEffect)(() => {
|
|
1936
|
-
if (!content || !Array.isArray(content.operations)) {
|
|
1937
|
-
lastSignatureRef.current = null;
|
|
1938
|
-
setOperations([]);
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
const incoming = content.operations;
|
|
1942
|
-
const signature = stringifyOperations(incoming);
|
|
1943
|
-
if (signature && signature === lastSignatureRef.current) return;
|
|
1944
|
-
lastSignatureRef.current = signature;
|
|
1945
|
-
setOperations(incoming);
|
|
1946
|
-
}, [content]);
|
|
1947
|
-
const groupedOperations = (0, react.useMemo)(() => {
|
|
1948
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1949
|
-
for (const operation of operations) {
|
|
1950
|
-
const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
|
|
1951
|
-
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
|
|
1952
|
-
groups.get(surfaceId).push(operation);
|
|
1953
|
-
}
|
|
1954
|
-
return groups;
|
|
1955
|
-
}, [operations]);
|
|
1956
|
-
if (!groupedOperations.size) return null;
|
|
1957
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1958
|
-
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
1959
|
-
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
|
|
1960
|
-
surfaceId,
|
|
1961
|
-
operations: ops,
|
|
1962
|
-
theme,
|
|
1963
|
-
agent,
|
|
1964
|
-
copilotkit
|
|
1965
|
-
}, surfaceId))
|
|
1966
|
-
});
|
|
1967
|
-
}
|
|
1968
|
-
};
|
|
1974
|
+
|
|
1975
|
+
//#endregion
|
|
1976
|
+
//#region src/v2/lib/processPartialHtml.ts
|
|
1977
|
+
/**
|
|
1978
|
+
* Extracts all complete `<style>` blocks from the raw HTML.
|
|
1979
|
+
* Returns the concatenated style tags, suitable for injection into `<head>`.
|
|
1980
|
+
*/
|
|
1981
|
+
function extractCompleteStyles(html) {
|
|
1982
|
+
const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
|
|
1983
|
+
return matches ? matches.join("") : "";
|
|
1969
1984
|
}
|
|
1970
1985
|
/**
|
|
1971
|
-
*
|
|
1972
|
-
*
|
|
1986
|
+
* Processes raw accumulated HTML for safe preview via innerHTML injection.
|
|
1987
|
+
* Pure function, no DOM dependencies.
|
|
1988
|
+
*
|
|
1989
|
+
* Pipeline (order matters):
|
|
1990
|
+
* 1. Strip incomplete tag at end
|
|
1991
|
+
* 2. Strip complete <style>, <script>, and <head> blocks
|
|
1992
|
+
* 3. Strip incomplete <style>/<script>/<head> blocks
|
|
1993
|
+
* 4. Strip incomplete HTML entities
|
|
1994
|
+
* 5. Extract body content (or use full string if no <body>)
|
|
1973
1995
|
*/
|
|
1974
|
-
function
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
await copilotkit.runAgent({ agent });
|
|
1987
|
-
} finally {
|
|
1988
|
-
if (copilotkit.properties) {
|
|
1989
|
-
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
1990
|
-
copilotkit.setProperties(rest);
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
}, [agent, copilotkit]),
|
|
1994
|
-
theme,
|
|
1995
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
|
|
1996
|
-
surfaceId,
|
|
1997
|
-
operations
|
|
1998
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
|
|
1999
|
-
surfaceId,
|
|
2000
|
-
className: "cpk:flex cpk:flex-1"
|
|
2001
|
-
})]
|
|
2002
|
-
})
|
|
2003
|
-
});
|
|
1996
|
+
function processPartialHtml(html) {
|
|
1997
|
+
let result = html;
|
|
1998
|
+
result = result.replace(/<[^>]*$/, "");
|
|
1999
|
+
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
2000
|
+
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
|
|
2001
|
+
result = result.replace(/&[a-zA-Z0-9#]*$/, "");
|
|
2002
|
+
const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
|
|
2003
|
+
if (bodyMatch) {
|
|
2004
|
+
result = bodyMatch[1];
|
|
2005
|
+
result = result.replace(/<\/body>[\s\S]*/i, "");
|
|
2006
|
+
}
|
|
2007
|
+
return result;
|
|
2004
2008
|
}
|
|
2009
|
+
|
|
2010
|
+
//#endregion
|
|
2011
|
+
//#region src/v2/components/OpenGenerativeUIRenderer.tsx
|
|
2012
|
+
const OpenGenerativeUIActivityType = "open-generative-ui";
|
|
2013
|
+
const OpenGenerativeUIContentSchema = zod.z.object({
|
|
2014
|
+
initialHeight: zod.z.number().optional(),
|
|
2015
|
+
generating: zod.z.boolean().optional(),
|
|
2016
|
+
css: zod.z.string().optional(),
|
|
2017
|
+
cssComplete: zod.z.boolean().optional(),
|
|
2018
|
+
html: zod.z.array(zod.z.string()).optional(),
|
|
2019
|
+
htmlComplete: zod.z.boolean().optional(),
|
|
2020
|
+
jsFunctions: zod.z.string().optional(),
|
|
2021
|
+
jsFunctionsComplete: zod.z.boolean().optional(),
|
|
2022
|
+
jsExpressions: zod.z.array(zod.z.string()).optional(),
|
|
2023
|
+
jsExpressionsComplete: zod.z.boolean().optional()
|
|
2024
|
+
});
|
|
2005
2025
|
/**
|
|
2006
|
-
*
|
|
2007
|
-
*
|
|
2026
|
+
* Schema for the generateSandboxedUi tool call arguments.
|
|
2027
|
+
* Used by the frontend tool renderer to display placeholder messages.
|
|
2008
2028
|
*/
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2029
|
+
const GenerateSandboxedUiArgsSchema = zod.z.object({
|
|
2030
|
+
initialHeight: zod.z.number().optional(),
|
|
2031
|
+
placeholderMessages: zod.z.array(zod.z.string()).optional(),
|
|
2032
|
+
css: zod.z.string().optional(),
|
|
2033
|
+
html: zod.z.string().optional(),
|
|
2034
|
+
jsFunctions: zod.z.string().optional(),
|
|
2035
|
+
jsExpressions: zod.z.array(zod.z.string()).optional()
|
|
2036
|
+
});
|
|
2037
|
+
const THROTTLE_MS = 1e3;
|
|
2038
|
+
/**
|
|
2039
|
+
* Returns true when the inner component should re-render immediately
|
|
2040
|
+
* (no throttle delay).
|
|
2041
|
+
*/
|
|
2042
|
+
function shouldFlushImmediately(prev, next) {
|
|
2043
|
+
if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
|
|
2044
|
+
if (next.htmlComplete) return true;
|
|
2045
|
+
if (next.generating === false) return true;
|
|
2046
|
+
if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
|
|
2047
|
+
if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
|
|
2048
|
+
if (next.html?.length && (!prev || !prev.html?.length)) return true;
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Outer wrapper — absorbs every parent re-render but only forwards
|
|
2053
|
+
* throttled content snapshots to the memoized inner component.
|
|
2054
|
+
*/
|
|
2055
|
+
const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
|
|
2056
|
+
const latestContentRef = (0, react.useRef)(content);
|
|
2057
|
+
latestContentRef.current = content;
|
|
2058
|
+
const [throttledContent, setThrottledContent] = (0, react.useState)(content);
|
|
2059
|
+
const throttledContentRef = (0, react.useRef)(throttledContent);
|
|
2060
|
+
const timerRef = (0, react.useRef)(null);
|
|
2061
|
+
if (throttledContentRef.current !== content) {
|
|
2062
|
+
if (shouldFlushImmediately(throttledContentRef.current, content)) {
|
|
2063
|
+
if (timerRef.current !== null) {
|
|
2064
|
+
clearTimeout(timerRef.current);
|
|
2065
|
+
timerRef.current = null;
|
|
2066
|
+
}
|
|
2067
|
+
throttledContentRef.current = content;
|
|
2068
|
+
setThrottledContent(content);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
const flush = (0, react.useCallback)(() => {
|
|
2072
|
+
timerRef.current = null;
|
|
2073
|
+
const latest = latestContentRef.current;
|
|
2074
|
+
throttledContentRef.current = latest;
|
|
2075
|
+
setThrottledContent(latest);
|
|
2076
|
+
}, []);
|
|
2077
|
+
(0, react.useEffect)(() => {
|
|
2078
|
+
if (throttledContentRef.current === content) return;
|
|
2079
|
+
if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
|
|
2080
|
+
}, [content, flush]);
|
|
2081
|
+
(0, react.useEffect)(() => {
|
|
2082
|
+
return () => {
|
|
2083
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
2084
|
+
};
|
|
2085
|
+
}, []);
|
|
2086
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
|
|
2087
|
+
};
|
|
2088
|
+
function ensureHead(html) {
|
|
2089
|
+
if (/<head[\s>]/i.test(html)) return html;
|
|
2090
|
+
return `<head></head>${html}`;
|
|
2091
|
+
}
|
|
2092
|
+
function injectCssIntoHtml(html, css) {
|
|
2093
|
+
const headCloseIdx = html.indexOf("</head>");
|
|
2094
|
+
if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
|
|
2095
|
+
return `<head><style>${css}</style></head>${html}`;
|
|
2096
|
+
}
|
|
2097
|
+
const OpenGenerativeUIActivityRendererInner = react.default.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
|
|
2098
|
+
const initialHeight = content.initialHeight ?? 200;
|
|
2099
|
+
const [autoHeight, setAutoHeight] = (0, react.useState)(null);
|
|
2100
|
+
const sandboxFunctions = useSandboxFunctions();
|
|
2101
|
+
const localApi = (0, react.useMemo)(() => {
|
|
2102
|
+
const api = {};
|
|
2103
|
+
for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
|
|
2104
|
+
return api;
|
|
2105
|
+
}, [sandboxFunctions]);
|
|
2106
|
+
const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
|
|
2107
|
+
const css = content.cssComplete ? content.css : void 0;
|
|
2108
|
+
const cssReady = !!content.cssComplete;
|
|
2109
|
+
const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
|
|
2110
|
+
const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
|
|
2111
|
+
const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
|
|
2112
|
+
const hasPreview = cssReady && !!previewBody?.trim();
|
|
2113
|
+
const hasVisibleSandbox = !!fullHtml || hasPreview;
|
|
2114
|
+
const containerRef = (0, react.useRef)(null);
|
|
2115
|
+
const sandboxRef = (0, react.useRef)(null);
|
|
2116
|
+
const previewSandboxRef = (0, react.useRef)(null);
|
|
2117
|
+
const previewReadyRef = (0, react.useRef)(false);
|
|
2118
|
+
const sandboxReadyRef = (0, react.useRef)(false);
|
|
2119
|
+
const executedIndexRef = (0, react.useRef)(0);
|
|
2120
|
+
const pendingQueueRef = (0, react.useRef)([]);
|
|
2121
|
+
const jsFunctionsInjectedRef = (0, react.useRef)(false);
|
|
2122
|
+
(0, react.useEffect)(() => {
|
|
2123
|
+
const container = containerRef.current;
|
|
2124
|
+
if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
|
|
2125
|
+
let cancelled = false;
|
|
2126
|
+
import("@jetbrains/websandbox").then((mod) => {
|
|
2127
|
+
if (cancelled) return;
|
|
2128
|
+
const sandbox = (mod.default?.default ?? mod.default).create({}, {
|
|
2129
|
+
frameContainer: container,
|
|
2130
|
+
frameContent: "<head></head><body></body>",
|
|
2131
|
+
allowAdditionalAttributes: ""
|
|
2132
|
+
});
|
|
2133
|
+
previewSandboxRef.current = sandbox;
|
|
2134
|
+
sandbox.iframe.style.width = "100%";
|
|
2135
|
+
sandbox.iframe.style.height = "100%";
|
|
2136
|
+
sandbox.iframe.style.border = "none";
|
|
2137
|
+
sandbox.iframe.style.backgroundColor = "transparent";
|
|
2138
|
+
sandbox.promise.then(() => {
|
|
2139
|
+
if (cancelled) return;
|
|
2140
|
+
previewReadyRef.current = true;
|
|
2141
|
+
sandbox.run(`
|
|
2142
|
+
var s = document.createElement('style');
|
|
2143
|
+
s.textContent = 'html, body { overflow: hidden !important; }';
|
|
2144
|
+
document.head.appendChild(s);
|
|
2145
|
+
`);
|
|
2146
|
+
const headParts = [];
|
|
2147
|
+
if (css) headParts.push(`<style>${css}</style>`);
|
|
2148
|
+
if (previewStyles) headParts.push(previewStyles);
|
|
2149
|
+
if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
|
|
2150
|
+
if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
|
|
2151
|
+
});
|
|
2152
|
+
}).catch((err) => {
|
|
2153
|
+
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
|
|
2154
|
+
});
|
|
2155
|
+
return () => {
|
|
2156
|
+
cancelled = true;
|
|
2157
|
+
};
|
|
2158
|
+
}, [hasPreview, fullHtml]);
|
|
2159
|
+
(0, react.useEffect)(() => {
|
|
2160
|
+
if (!previewSandboxRef.current || !previewReadyRef.current) return;
|
|
2161
|
+
const headParts = [];
|
|
2162
|
+
if (css) headParts.push(`<style>${css}</style>`);
|
|
2163
|
+
if (previewStyles) headParts.push(previewStyles);
|
|
2164
|
+
if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
|
|
2165
|
+
if (!previewBody) return;
|
|
2166
|
+
previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
|
|
2167
|
+
}, [
|
|
2168
|
+
previewBody,
|
|
2169
|
+
previewStyles,
|
|
2170
|
+
css
|
|
2171
|
+
]);
|
|
2172
|
+
(0, react.useEffect)(() => {
|
|
2173
|
+
const container = containerRef.current;
|
|
2174
|
+
if (!container || !fullHtml) return;
|
|
2175
|
+
if (previewSandboxRef.current) {
|
|
2176
|
+
previewSandboxRef.current.destroy();
|
|
2177
|
+
previewSandboxRef.current = null;
|
|
2178
|
+
previewReadyRef.current = false;
|
|
2179
|
+
}
|
|
2180
|
+
let cancelled = false;
|
|
2181
|
+
executedIndexRef.current = 0;
|
|
2182
|
+
jsFunctionsInjectedRef.current = false;
|
|
2183
|
+
sandboxReadyRef.current = false;
|
|
2184
|
+
pendingQueueRef.current = [];
|
|
2185
|
+
const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
|
|
2186
|
+
import("@jetbrains/websandbox").then((mod) => {
|
|
2187
|
+
if (cancelled) return;
|
|
2188
|
+
const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
|
|
2189
|
+
frameContainer: container,
|
|
2190
|
+
frameContent: ensureHead(htmlContent),
|
|
2191
|
+
allowAdditionalAttributes: ""
|
|
2192
|
+
});
|
|
2193
|
+
sandboxRef.current = sandbox;
|
|
2194
|
+
sandbox.iframe.style.width = "100%";
|
|
2195
|
+
sandbox.iframe.style.height = "100%";
|
|
2196
|
+
sandbox.iframe.style.border = "none";
|
|
2197
|
+
sandbox.iframe.style.backgroundColor = "transparent";
|
|
2198
|
+
sandbox.promise.then(() => {
|
|
2199
|
+
if (cancelled) return;
|
|
2200
|
+
sandboxReadyRef.current = true;
|
|
2201
|
+
sandbox.run(`
|
|
2202
|
+
var s = document.createElement('style');
|
|
2203
|
+
s.textContent = 'html, body { overflow: hidden !important; }';
|
|
2204
|
+
document.head.appendChild(s);
|
|
2205
|
+
`);
|
|
2206
|
+
const queue = pendingQueueRef.current;
|
|
2207
|
+
pendingQueueRef.current = [];
|
|
2208
|
+
for (const code of queue) sandbox.run(code);
|
|
2209
|
+
});
|
|
2210
|
+
}).catch((err) => {
|
|
2211
|
+
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
|
|
2212
|
+
});
|
|
2213
|
+
return () => {
|
|
2214
|
+
cancelled = true;
|
|
2215
|
+
if (previewSandboxRef.current) {
|
|
2216
|
+
previewSandboxRef.current.destroy();
|
|
2217
|
+
previewSandboxRef.current = null;
|
|
2218
|
+
previewReadyRef.current = false;
|
|
2219
|
+
}
|
|
2220
|
+
if (sandboxRef.current) {
|
|
2221
|
+
sandboxRef.current.destroy();
|
|
2222
|
+
sandboxRef.current = null;
|
|
2223
|
+
}
|
|
2224
|
+
sandboxReadyRef.current = false;
|
|
2225
|
+
setAutoHeight(null);
|
|
2226
|
+
};
|
|
2227
|
+
}, [
|
|
2228
|
+
fullHtml,
|
|
2229
|
+
css,
|
|
2230
|
+
localApi
|
|
2231
|
+
]);
|
|
2232
|
+
(0, react.useEffect)(() => {
|
|
2233
|
+
if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
|
|
2234
|
+
jsFunctionsInjectedRef.current = true;
|
|
2235
|
+
const sandbox = sandboxRef.current;
|
|
2236
|
+
if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
|
|
2237
|
+
else pendingQueueRef.current.push(content.jsFunctions);
|
|
2238
|
+
}, [content.jsFunctions]);
|
|
2239
|
+
(0, react.useEffect)(() => {
|
|
2240
|
+
const expressions = content.jsExpressions;
|
|
2241
|
+
if (!expressions || expressions.length === 0) return;
|
|
2242
|
+
const startIndex = executedIndexRef.current;
|
|
2243
|
+
if (startIndex >= expressions.length) return;
|
|
2244
|
+
const newExprs = expressions.slice(startIndex);
|
|
2245
|
+
executedIndexRef.current = expressions.length;
|
|
2246
|
+
const sandbox = sandboxRef.current;
|
|
2247
|
+
if (sandboxReadyRef.current && sandbox) (async () => {
|
|
2248
|
+
for (const expr of newExprs) await sandbox.run(expr);
|
|
2249
|
+
})();
|
|
2250
|
+
else pendingQueueRef.current.push(...newExprs);
|
|
2251
|
+
}, [content.jsExpressions?.length]);
|
|
2252
|
+
const generationDone = content.generating === false;
|
|
2253
|
+
(0, react.useEffect)(() => {
|
|
2254
|
+
const sandbox = sandboxRef.current;
|
|
2255
|
+
if (!generationDone || !sandbox) return;
|
|
2256
|
+
let handled = false;
|
|
2257
|
+
const onMessage = (e) => {
|
|
2258
|
+
if (handled) return;
|
|
2259
|
+
if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
|
|
2260
|
+
handled = true;
|
|
2261
|
+
setAutoHeight(e.data.height);
|
|
2262
|
+
window.removeEventListener("message", onMessage);
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
2265
|
+
window.addEventListener("message", onMessage);
|
|
2266
|
+
const measureOnce = `
|
|
2267
|
+
(function() {
|
|
2268
|
+
var s = document.createElement('style');
|
|
2269
|
+
s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
|
|
2270
|
+
document.head.appendChild(s);
|
|
2271
|
+
var h = document.body.scrollHeight;
|
|
2272
|
+
var cs = getComputedStyle(document.body);
|
|
2273
|
+
h += parseFloat(cs.marginTop) || 0;
|
|
2274
|
+
h += parseFloat(cs.marginBottom) || 0;
|
|
2275
|
+
s.remove();
|
|
2276
|
+
parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
|
|
2277
|
+
})();
|
|
2278
|
+
`;
|
|
2279
|
+
if (sandboxReadyRef.current) sandbox.run(measureOnce);
|
|
2280
|
+
else pendingQueueRef.current.push(measureOnce);
|
|
2281
|
+
return () => {
|
|
2282
|
+
window.removeEventListener("message", onMessage);
|
|
2283
|
+
};
|
|
2284
|
+
}, [generationDone]);
|
|
2285
|
+
const height = autoHeight ?? initialHeight;
|
|
2286
|
+
const isGenerating = content.generating !== false;
|
|
2287
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2288
|
+
ref: containerRef,
|
|
2289
|
+
style: {
|
|
2290
|
+
position: "relative",
|
|
2291
|
+
width: "100%",
|
|
2292
|
+
height: `${height}px`,
|
|
2293
|
+
borderRadius: "8px",
|
|
2294
|
+
backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
|
|
2295
|
+
border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
|
|
2296
|
+
display: hasVisibleSandbox ? "block" : "flex",
|
|
2297
|
+
alignItems: hasVisibleSandbox ? void 0 : "center",
|
|
2298
|
+
justifyContent: hasVisibleSandbox ? void 0 : "center",
|
|
2299
|
+
overflow: "hidden"
|
|
2300
|
+
},
|
|
2301
|
+
children: isGenerating && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2302
|
+
style: {
|
|
2303
|
+
position: "absolute",
|
|
2304
|
+
inset: 0,
|
|
2305
|
+
zIndex: 10,
|
|
2306
|
+
pointerEvents: "all",
|
|
2307
|
+
backgroundColor: "rgba(255, 255, 255, 0.5)",
|
|
2308
|
+
display: "flex",
|
|
2309
|
+
alignItems: "center",
|
|
2310
|
+
justifyContent: "center"
|
|
2311
|
+
},
|
|
2312
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
2313
|
+
width: "48",
|
|
2314
|
+
height: "48",
|
|
2315
|
+
viewBox: "0 0 24 24",
|
|
2316
|
+
fill: "none",
|
|
2317
|
+
style: { animation: "ck-spin 1s linear infinite" },
|
|
2318
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
2319
|
+
cx: "12",
|
|
2320
|
+
cy: "12",
|
|
2321
|
+
r: "10",
|
|
2322
|
+
stroke: "#e0e0e0",
|
|
2323
|
+
strokeWidth: "3"
|
|
2324
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
2325
|
+
d: "M12 2a10 10 0 0 1 10 10",
|
|
2326
|
+
stroke: "#999",
|
|
2327
|
+
strokeWidth: "3",
|
|
2328
|
+
strokeLinecap: "round"
|
|
2329
|
+
})]
|
|
2330
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
|
|
2331
|
+
})
|
|
2332
|
+
});
|
|
2333
|
+
}, (prev, next) => prev.content === next.content);
|
|
2334
|
+
/**
|
|
2335
|
+
* Frontend tool renderer for generateSandboxedUi.
|
|
2336
|
+
* Displays placeholder messages while the UI is being generated.
|
|
2337
|
+
*/
|
|
2338
|
+
const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
|
|
2339
|
+
const [visibleMessageIndex, setVisibleMessageIndex] = (0, react.useState)(0);
|
|
2340
|
+
const prevMessageCountRef = (0, react.useRef)(0);
|
|
2341
|
+
const messages = props.args.placeholderMessages;
|
|
2342
|
+
(0, react.useEffect)(() => {
|
|
2343
|
+
if (!messages || messages.length === 0) return;
|
|
2344
|
+
if (messages.length !== prevMessageCountRef.current) {
|
|
2345
|
+
prevMessageCountRef.current = messages.length;
|
|
2346
|
+
setVisibleMessageIndex(messages.length - 1);
|
|
2347
|
+
}
|
|
2348
|
+
if (props.status === _copilotkit_core.ToolCallStatus.Complete) return;
|
|
2349
|
+
const timer = setInterval(() => {
|
|
2350
|
+
setVisibleMessageIndex((i) => (i + 1) % messages.length);
|
|
2351
|
+
}, 5e3);
|
|
2352
|
+
return () => clearInterval(timer);
|
|
2353
|
+
}, [messages?.length, props.status]);
|
|
2354
|
+
if (props.status === _copilotkit_core.ToolCallStatus.Complete) return null;
|
|
2355
|
+
if (!messages || messages.length === 0) return null;
|
|
2356
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2357
|
+
style: {
|
|
2358
|
+
padding: "8px 12px",
|
|
2359
|
+
color: "#999",
|
|
2360
|
+
fontSize: "14px"
|
|
2361
|
+
},
|
|
2362
|
+
children: messages[visibleMessageIndex] ?? messages[0]
|
|
2363
|
+
});
|
|
2364
|
+
};
|
|
2365
|
+
|
|
2366
|
+
//#endregion
|
|
2367
|
+
//#region src/v2/a2ui/A2UIMessageRenderer.tsx
|
|
2368
|
+
/**
|
|
2369
|
+
* The container key used to wrap A2UI operations for explicit detection.
|
|
2370
|
+
* Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
|
|
2371
|
+
*/
|
|
2372
|
+
const A2UI_OPERATIONS_KEY = "a2ui_operations";
|
|
2373
|
+
let initialized = false;
|
|
2374
|
+
function ensureInitialized() {
|
|
2375
|
+
if (!initialized) {
|
|
2376
|
+
(0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
|
|
2377
|
+
(0, _copilotkit_a2ui_renderer.injectStyles)();
|
|
2378
|
+
initialized = true;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
function createA2UIMessageRenderer(options) {
|
|
2382
|
+
const { theme, catalog, loadingComponent } = options;
|
|
2383
|
+
return {
|
|
2384
|
+
activityType: "a2ui-surface",
|
|
2385
|
+
content: zod.z.any(),
|
|
2386
|
+
render: ({ content, agent }) => {
|
|
2387
|
+
ensureInitialized();
|
|
2388
|
+
const [operations, setOperations] = (0, react.useState)([]);
|
|
2389
|
+
const { copilotkit } = useCopilotKit();
|
|
2390
|
+
const lastContentRef = (0, react.useRef)(null);
|
|
2391
|
+
(0, react.useEffect)(() => {
|
|
2392
|
+
if (content === lastContentRef.current) return;
|
|
2393
|
+
lastContentRef.current = content;
|
|
2394
|
+
const incoming = content?.[A2UI_OPERATIONS_KEY];
|
|
2395
|
+
if (!content || !Array.isArray(incoming)) {
|
|
2396
|
+
setOperations([]);
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
setOperations(incoming);
|
|
2400
|
+
}, [content]);
|
|
2401
|
+
const groupedOperations = (0, react.useMemo)(() => {
|
|
2402
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2403
|
+
for (const operation of operations) {
|
|
2404
|
+
const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
|
|
2405
|
+
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
|
|
2406
|
+
groups.get(surfaceId).push(operation);
|
|
2407
|
+
}
|
|
2408
|
+
return groups;
|
|
2409
|
+
}, [operations]);
|
|
2410
|
+
if (!groupedOperations.size) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(loadingComponent ?? DefaultA2UILoading, {});
|
|
2411
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2412
|
+
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
2413
|
+
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
|
|
2414
|
+
surfaceId,
|
|
2415
|
+
operations: ops,
|
|
2416
|
+
theme,
|
|
2417
|
+
agent,
|
|
2418
|
+
copilotkit,
|
|
2419
|
+
catalog
|
|
2420
|
+
}, surfaceId))
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Renders a single A2UI surface using the React renderer.
|
|
2427
|
+
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
2428
|
+
*/
|
|
2429
|
+
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog }) {
|
|
2430
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2431
|
+
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
2432
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
|
|
2433
|
+
onAction: (0, react.useCallback)(async (message) => {
|
|
2434
|
+
if (!agent) return;
|
|
2435
|
+
message.userAction;
|
|
2436
|
+
try {
|
|
2437
|
+
copilotkit.setProperties({
|
|
2438
|
+
...copilotkit.properties ?? {},
|
|
2439
|
+
a2uiAction: message
|
|
2440
|
+
});
|
|
2441
|
+
await copilotkit.runAgent({ agent });
|
|
2442
|
+
} finally {
|
|
2443
|
+
if (copilotkit.properties) {
|
|
2444
|
+
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
2445
|
+
copilotkit.setProperties(rest);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}, [agent, copilotkit]),
|
|
2449
|
+
theme,
|
|
2450
|
+
catalog,
|
|
2451
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
|
|
2452
|
+
surfaceId,
|
|
2453
|
+
operations
|
|
2454
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UISurfaceOrError, { surfaceId })]
|
|
2455
|
+
})
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Renders the A2UI surface, or an error message if processing failed.
|
|
2460
|
+
* Must be a child of A2UIProvider to access the error state.
|
|
2461
|
+
*/
|
|
2462
|
+
function A2UISurfaceOrError({ surfaceId }) {
|
|
2463
|
+
const error = (0, _copilotkit_a2ui_renderer.useA2UIError)();
|
|
2464
|
+
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2465
|
+
className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
|
|
2466
|
+
children: ["A2UI render error: ", error]
|
|
2467
|
+
});
|
|
2468
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
|
|
2469
|
+
surfaceId,
|
|
2470
|
+
className: "cpk:flex cpk:flex-1"
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
/**
|
|
2474
|
+
* Processes A2UI operations into the provider's message processor.
|
|
2475
|
+
* Must be a child of A2UIProvider to access the actions context.
|
|
2476
|
+
*/
|
|
2477
|
+
function SurfaceMessageProcessor({ surfaceId, operations }) {
|
|
2478
|
+
const { processMessages, getSurface } = (0, _copilotkit_a2ui_renderer.useA2UIActions)();
|
|
2479
|
+
const lastHashRef = (0, react.useRef)("");
|
|
2480
|
+
(0, react.useEffect)(() => {
|
|
2481
|
+
const hash = JSON.stringify(operations);
|
|
2482
|
+
if (hash === lastHashRef.current) return;
|
|
2483
|
+
lastHashRef.current = hash;
|
|
2484
|
+
processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
|
|
2017
2485
|
}, [
|
|
2018
2486
|
processMessages,
|
|
2487
|
+
getSurface,
|
|
2019
2488
|
surfaceId,
|
|
2020
2489
|
operations
|
|
2021
2490
|
]);
|
|
2022
2491
|
return null;
|
|
2023
2492
|
}
|
|
2493
|
+
/**
|
|
2494
|
+
* Default loading component shown while an A2UI surface is generating.
|
|
2495
|
+
* Displays an animated shimmer skeleton.
|
|
2496
|
+
*/
|
|
2497
|
+
function DefaultA2UILoading() {
|
|
2498
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2499
|
+
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",
|
|
2500
|
+
style: { minHeight: 120 },
|
|
2501
|
+
children: [
|
|
2502
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2503
|
+
className: "cpk:flex cpk:items-center cpk:gap-2",
|
|
2504
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2505
|
+
className: "cpk:h-3 cpk:w-3 cpk:rounded-full cpk:bg-gray-200",
|
|
2506
|
+
style: { animation: "cpk-a2ui-pulse 1.5s ease-in-out infinite" }
|
|
2507
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2508
|
+
className: "cpk:text-xs cpk:font-medium cpk:text-gray-400",
|
|
2509
|
+
children: "Generating UI..."
|
|
2510
|
+
})]
|
|
2511
|
+
}),
|
|
2512
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2513
|
+
className: "cpk:flex cpk:flex-col cpk:gap-2",
|
|
2514
|
+
children: [
|
|
2515
|
+
.8,
|
|
2516
|
+
.6,
|
|
2517
|
+
.4
|
|
2518
|
+
].map((width, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2519
|
+
className: "cpk:h-3 cpk:rounded cpk:bg-gray-200/70",
|
|
2520
|
+
style: {
|
|
2521
|
+
width: `${width * 100}%`,
|
|
2522
|
+
animation: `cpk-a2ui-pulse 1.5s ease-in-out ${i * .15}s infinite`
|
|
2523
|
+
}
|
|
2524
|
+
}, i))
|
|
2525
|
+
}),
|
|
2526
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
|
|
2527
|
+
@keyframes cpk-a2ui-pulse {
|
|
2528
|
+
0%, 100% { opacity: 0.4; }
|
|
2529
|
+
50% { opacity: 1; }
|
|
2530
|
+
}
|
|
2531
|
+
` })
|
|
2532
|
+
]
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2024
2535
|
function getOperationSurfaceId(operation) {
|
|
2025
2536
|
if (!operation || typeof operation !== "object") return null;
|
|
2026
2537
|
if (typeof operation.surfaceId === "string") return operation.surfaceId;
|
|
2027
|
-
return operation?.
|
|
2538
|
+
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
2028
2539
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2540
|
+
|
|
2541
|
+
//#endregion
|
|
2542
|
+
//#region src/v2/types/defineToolCallRenderer.ts
|
|
2543
|
+
function defineToolCallRenderer(def) {
|
|
2544
|
+
const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
|
|
2545
|
+
return {
|
|
2546
|
+
name: def.name,
|
|
2547
|
+
args: argsSchema,
|
|
2548
|
+
render: def.render,
|
|
2549
|
+
...def.agentId ? { agentId: def.agentId } : {}
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
//#endregion
|
|
2554
|
+
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
2555
|
+
/**
|
|
2556
|
+
* Tool name used by the dynamic A2UI generation secondary LLM.
|
|
2557
|
+
* This renderer is auto-registered when A2UI is enabled.
|
|
2558
|
+
*/
|
|
2559
|
+
const RENDER_A2UI_TOOL_NAME = "render_a2ui";
|
|
2560
|
+
/**
|
|
2561
|
+
* Built-in progress indicator for dynamic A2UI generation.
|
|
2562
|
+
* Shows a skeleton wireframe that progressively reveals as tokens stream in.
|
|
2563
|
+
*
|
|
2564
|
+
* Registered automatically when A2UI is enabled. Users can override by
|
|
2565
|
+
* providing their own `useRenderTool({ name: "render_a2ui", ... })`.
|
|
2566
|
+
*/
|
|
2567
|
+
function A2UIProgressIndicator({ parameters }) {
|
|
2568
|
+
const lastRef = (0, react.useRef)({
|
|
2569
|
+
time: 0,
|
|
2570
|
+
tokens: 0
|
|
2571
|
+
});
|
|
2572
|
+
const now = Date.now();
|
|
2573
|
+
let { tokens } = lastRef.current;
|
|
2574
|
+
if (now - lastRef.current.time > 200) {
|
|
2575
|
+
const chars = JSON.stringify(parameters ?? {}).length;
|
|
2576
|
+
tokens = Math.round(chars / 4);
|
|
2577
|
+
lastRef.current = {
|
|
2578
|
+
time: now,
|
|
2579
|
+
tokens
|
|
2580
|
+
};
|
|
2034
2581
|
}
|
|
2582
|
+
const phase = tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
|
|
2583
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2584
|
+
style: {
|
|
2585
|
+
margin: "12px 0",
|
|
2586
|
+
maxWidth: 320
|
|
2587
|
+
},
|
|
2588
|
+
children: [
|
|
2589
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2590
|
+
style: {
|
|
2591
|
+
position: "relative",
|
|
2592
|
+
overflow: "hidden",
|
|
2593
|
+
borderRadius: 12,
|
|
2594
|
+
border: "1px solid rgba(228,228,231,0.8)",
|
|
2595
|
+
backgroundColor: "#fff",
|
|
2596
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
2597
|
+
padding: "16px 18px 14px"
|
|
2598
|
+
},
|
|
2599
|
+
children: [
|
|
2600
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2601
|
+
style: {
|
|
2602
|
+
display: "flex",
|
|
2603
|
+
alignItems: "center",
|
|
2604
|
+
gap: 8,
|
|
2605
|
+
marginBottom: 12
|
|
2606
|
+
},
|
|
2607
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2608
|
+
style: {
|
|
2609
|
+
display: "flex",
|
|
2610
|
+
gap: 4
|
|
2611
|
+
},
|
|
2612
|
+
children: [
|
|
2613
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2614
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2615
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {})
|
|
2616
|
+
]
|
|
2617
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2618
|
+
w: 64,
|
|
2619
|
+
h: 6,
|
|
2620
|
+
bg: "#e4e4e7",
|
|
2621
|
+
opacity: phase >= 1 ? 1 : .4,
|
|
2622
|
+
transition: "opacity 0.5s"
|
|
2623
|
+
})]
|
|
2624
|
+
}),
|
|
2625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2626
|
+
style: {
|
|
2627
|
+
display: "grid",
|
|
2628
|
+
gap: 7
|
|
2629
|
+
},
|
|
2630
|
+
children: [
|
|
2631
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2632
|
+
show: phase >= 0,
|
|
2633
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2634
|
+
w: 36,
|
|
2635
|
+
h: 7,
|
|
2636
|
+
bg: "rgba(147,197,253,0.7)",
|
|
2637
|
+
anim: 0
|
|
2638
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2639
|
+
w: 80,
|
|
2640
|
+
h: 7,
|
|
2641
|
+
bg: "rgba(219,234,254,0.8)",
|
|
2642
|
+
anim: .2
|
|
2643
|
+
})]
|
|
2644
|
+
}),
|
|
2645
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2646
|
+
show: phase >= 0,
|
|
2647
|
+
delay: .1,
|
|
2648
|
+
children: [
|
|
2649
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
|
|
2650
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2651
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2652
|
+
w: 100,
|
|
2653
|
+
h: 7,
|
|
2654
|
+
bg: "rgba(24,24,27,0.2)",
|
|
2655
|
+
anim: .3
|
|
2656
|
+
})
|
|
2657
|
+
]
|
|
2658
|
+
}),
|
|
2659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2660
|
+
show: phase >= 1,
|
|
2661
|
+
delay: .15,
|
|
2662
|
+
children: [
|
|
2663
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
|
|
2664
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2665
|
+
w: 48,
|
|
2666
|
+
h: 7,
|
|
2667
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2668
|
+
anim: .1
|
|
2669
|
+
}),
|
|
2670
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2671
|
+
w: 40,
|
|
2672
|
+
h: 7,
|
|
2673
|
+
bg: "rgba(153,246,228,0.6)",
|
|
2674
|
+
anim: .5
|
|
2675
|
+
}),
|
|
2676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2677
|
+
w: 56,
|
|
2678
|
+
h: 7,
|
|
2679
|
+
bg: "rgba(147,197,253,0.6)",
|
|
2680
|
+
anim: .3
|
|
2681
|
+
})
|
|
2682
|
+
]
|
|
2683
|
+
}),
|
|
2684
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2685
|
+
show: phase >= 1,
|
|
2686
|
+
delay: .2,
|
|
2687
|
+
children: [
|
|
2688
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
|
|
2689
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2690
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2691
|
+
w: 60,
|
|
2692
|
+
h: 7,
|
|
2693
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2694
|
+
anim: .4
|
|
2695
|
+
})
|
|
2696
|
+
]
|
|
2697
|
+
}),
|
|
2698
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2699
|
+
show: phase >= 2,
|
|
2700
|
+
delay: .25,
|
|
2701
|
+
children: [
|
|
2702
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2703
|
+
w: 40,
|
|
2704
|
+
h: 7,
|
|
2705
|
+
bg: "rgba(153,246,228,0.5)",
|
|
2706
|
+
anim: .2
|
|
2707
|
+
}),
|
|
2708
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2709
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2710
|
+
w: 48,
|
|
2711
|
+
h: 7,
|
|
2712
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2713
|
+
anim: .6
|
|
2714
|
+
}),
|
|
2715
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2716
|
+
w: 64,
|
|
2717
|
+
h: 7,
|
|
2718
|
+
bg: "rgba(147,197,253,0.5)",
|
|
2719
|
+
anim: .1
|
|
2720
|
+
})
|
|
2721
|
+
]
|
|
2722
|
+
}),
|
|
2723
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2724
|
+
show: phase >= 2,
|
|
2725
|
+
delay: .3,
|
|
2726
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2727
|
+
w: 36,
|
|
2728
|
+
h: 7,
|
|
2729
|
+
bg: "rgba(147,197,253,0.6)",
|
|
2730
|
+
anim: .5
|
|
2731
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2732
|
+
w: 36,
|
|
2733
|
+
h: 7,
|
|
2734
|
+
bg: "rgba(24,24,27,0.12)",
|
|
2735
|
+
anim: .7
|
|
2736
|
+
})]
|
|
2737
|
+
}),
|
|
2738
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
|
|
2739
|
+
show: phase >= 3,
|
|
2740
|
+
delay: .35,
|
|
2741
|
+
children: [
|
|
2742
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2744
|
+
w: 44,
|
|
2745
|
+
h: 7,
|
|
2746
|
+
bg: "rgba(24,24,27,0.18)",
|
|
2747
|
+
anim: .3
|
|
2748
|
+
}),
|
|
2749
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
|
|
2750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2751
|
+
w: 56,
|
|
2752
|
+
h: 7,
|
|
2753
|
+
bg: "rgba(153,246,228,0.5)",
|
|
2754
|
+
anim: .8
|
|
2755
|
+
}),
|
|
2756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
|
|
2757
|
+
w: 48,
|
|
2758
|
+
h: 7,
|
|
2759
|
+
bg: "rgba(147,197,253,0.5)",
|
|
2760
|
+
anim: .4
|
|
2761
|
+
})
|
|
2762
|
+
]
|
|
2763
|
+
})
|
|
2764
|
+
]
|
|
2765
|
+
}),
|
|
2766
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
2767
|
+
pointerEvents: "none",
|
|
2768
|
+
position: "absolute",
|
|
2769
|
+
inset: 0,
|
|
2770
|
+
background: "linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
|
|
2771
|
+
backgroundSize: "250% 100%",
|
|
2772
|
+
animation: "cpk-a2ui-sweep 3s ease-in-out infinite"
|
|
2773
|
+
} })
|
|
2774
|
+
]
|
|
2775
|
+
}),
|
|
2776
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2777
|
+
style: {
|
|
2778
|
+
display: "flex",
|
|
2779
|
+
alignItems: "center",
|
|
2780
|
+
justifyContent: "center",
|
|
2781
|
+
gap: 8,
|
|
2782
|
+
marginTop: 8
|
|
2783
|
+
},
|
|
2784
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2785
|
+
style: {
|
|
2786
|
+
fontSize: 12,
|
|
2787
|
+
color: "#a1a1aa",
|
|
2788
|
+
letterSpacing: "0.025em"
|
|
2789
|
+
},
|
|
2790
|
+
children: "Building interface"
|
|
2791
|
+
}), tokens > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2792
|
+
style: {
|
|
2793
|
+
fontSize: 11,
|
|
2794
|
+
color: "#d4d4d8",
|
|
2795
|
+
fontVariantNumeric: "tabular-nums"
|
|
2796
|
+
},
|
|
2797
|
+
children: [
|
|
2798
|
+
"~",
|
|
2799
|
+
tokens.toLocaleString(),
|
|
2800
|
+
" tokens"
|
|
2801
|
+
]
|
|
2802
|
+
})]
|
|
2803
|
+
}),
|
|
2804
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
|
|
2805
|
+
@keyframes cpk-a2ui-fade {
|
|
2806
|
+
0%, 100% { opacity: 1; }
|
|
2807
|
+
50% { opacity: 0.5; }
|
|
2808
|
+
}
|
|
2809
|
+
@keyframes cpk-a2ui-sweep {
|
|
2810
|
+
0% { background-position: 250% 0; }
|
|
2811
|
+
100% { background-position: -250% 0; }
|
|
2812
|
+
}
|
|
2813
|
+
` })
|
|
2814
|
+
]
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
function Dot() {
|
|
2818
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
2819
|
+
width: 7,
|
|
2820
|
+
height: 7,
|
|
2821
|
+
borderRadius: "50%",
|
|
2822
|
+
backgroundColor: "#d4d4d8",
|
|
2823
|
+
flexShrink: 0
|
|
2824
|
+
} });
|
|
2825
|
+
}
|
|
2826
|
+
function Spacer() {
|
|
2827
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { width: 12 } });
|
|
2828
|
+
}
|
|
2829
|
+
function Bar({ w, h, bg, anim, opacity, transition }) {
|
|
2830
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
2831
|
+
width: w,
|
|
2832
|
+
height: h,
|
|
2833
|
+
borderRadius: 9999,
|
|
2834
|
+
backgroundColor: bg,
|
|
2835
|
+
...anim !== void 0 ? { animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite` } : {},
|
|
2836
|
+
...opacity !== void 0 ? { opacity } : {},
|
|
2837
|
+
...transition ? { transition } : {}
|
|
2838
|
+
} });
|
|
2839
|
+
}
|
|
2840
|
+
function Row({ children, show, delay = 0 }) {
|
|
2841
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2842
|
+
style: {
|
|
2843
|
+
display: "flex",
|
|
2844
|
+
alignItems: "center",
|
|
2845
|
+
gap: 6,
|
|
2846
|
+
opacity: show ? 1 : 0,
|
|
2847
|
+
transition: `opacity 0.4s ${delay}s`
|
|
2848
|
+
},
|
|
2849
|
+
children
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Registers the built-in `render_a2ui` tool call renderer via the props-based
|
|
2854
|
+
* `setRenderToolCalls` mechanism (not `useRenderTool`).
|
|
2855
|
+
*
|
|
2856
|
+
* This ensures user-registered `useRenderTool({ name: "render_a2ui", ... })`
|
|
2857
|
+
* hooks automatically override the built-in, since the merge logic in
|
|
2858
|
+
* react-core.ts gives hook-based entries priority over prop-based entries.
|
|
2859
|
+
*/
|
|
2860
|
+
function A2UIBuiltInToolCallRenderer() {
|
|
2861
|
+
const { copilotkit } = useCopilotKit();
|
|
2862
|
+
(0, react.useEffect)(() => {
|
|
2863
|
+
const renderer = defineToolCallRenderer({
|
|
2864
|
+
name: RENDER_A2UI_TOOL_NAME,
|
|
2865
|
+
args: zod.z.any(),
|
|
2866
|
+
render: ({ status, args: parameters }) => {
|
|
2867
|
+
if (status === "complete") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
2868
|
+
const params = parameters;
|
|
2869
|
+
const items = params?.items;
|
|
2870
|
+
if (Array.isArray(items) && items.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
2871
|
+
const components = params?.components;
|
|
2872
|
+
if (Array.isArray(components) && components.length > 2) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
2873
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIProgressIndicator, { parameters });
|
|
2874
|
+
}
|
|
2875
|
+
});
|
|
2876
|
+
const existing = copilotkit._renderToolCalls ?? [];
|
|
2877
|
+
copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
|
|
2878
|
+
}, [copilotkit]);
|
|
2879
|
+
return null;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
//#endregion
|
|
2883
|
+
//#region src/v2/hooks/use-agent-context.tsx
|
|
2884
|
+
function useAgentContext(context) {
|
|
2885
|
+
const { description, value } = context;
|
|
2886
|
+
const { copilotkit } = useCopilotKit();
|
|
2887
|
+
const stringValue = (0, react.useMemo)(() => {
|
|
2888
|
+
if (typeof value === "string") return value;
|
|
2889
|
+
return JSON.stringify(value);
|
|
2890
|
+
}, [value]);
|
|
2891
|
+
(0, react.useLayoutEffect)(() => {
|
|
2892
|
+
if (!copilotkit) return;
|
|
2893
|
+
const id = copilotkit.addContext({
|
|
2894
|
+
description,
|
|
2895
|
+
value: stringValue
|
|
2896
|
+
});
|
|
2897
|
+
return () => {
|
|
2898
|
+
copilotkit.removeContext(id);
|
|
2899
|
+
};
|
|
2900
|
+
}, [
|
|
2901
|
+
description,
|
|
2902
|
+
stringValue,
|
|
2903
|
+
copilotkit
|
|
2904
|
+
]);
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
//#endregion
|
|
2908
|
+
//#region src/v2/a2ui/A2UICatalogContext.tsx
|
|
2909
|
+
/**
|
|
2910
|
+
* Renders agent context describing the available A2UI catalog and custom components.
|
|
2911
|
+
* Only mount this component when A2UI is enabled.
|
|
2912
|
+
*
|
|
2913
|
+
* When `includeSchema` is true, the full component schemas (JSON Schema) are also
|
|
2914
|
+
* sent as context using the same description key as the A2UI middleware, so the
|
|
2915
|
+
* middleware can optionally overwrite it with a server-side schema.
|
|
2916
|
+
*/
|
|
2917
|
+
function A2UICatalogContext({ catalog, includeSchema }) {
|
|
2918
|
+
useAgentContext({
|
|
2919
|
+
description: "A2UI catalog capabilities: available catalog IDs and custom component definitions the client can render.",
|
|
2920
|
+
value: (0, _copilotkit_a2ui_renderer.buildCatalogContextValue)(catalog)
|
|
2921
|
+
});
|
|
2922
|
+
const { copilotkit } = useCopilotKit();
|
|
2923
|
+
const schemaValue = (0, react.useMemo)(() => includeSchema !== false ? JSON.stringify((0, _copilotkit_a2ui_renderer.extractCatalogComponentSchemas)(catalog)) : null, [catalog, includeSchema]);
|
|
2924
|
+
(0, react.useLayoutEffect)(() => {
|
|
2925
|
+
if (!copilotkit || !schemaValue) return;
|
|
2926
|
+
const ids = [];
|
|
2927
|
+
ids.push(copilotkit.addContext({
|
|
2928
|
+
description: _copilotkit_a2ui_renderer.A2UI_SCHEMA_CONTEXT_DESCRIPTION,
|
|
2929
|
+
value: schemaValue
|
|
2930
|
+
}));
|
|
2931
|
+
ids.push(copilotkit.addContext({
|
|
2932
|
+
description: "A2UI generation guidelines — protocol rules, tool arguments, path rules, data model format, and form/two-way-binding instructions.",
|
|
2933
|
+
value: _copilotkit_shared.A2UI_DEFAULT_GENERATION_GUIDELINES
|
|
2934
|
+
}));
|
|
2935
|
+
ids.push(copilotkit.addContext({
|
|
2936
|
+
description: "A2UI design guidelines — visual design rules, component hierarchy tips, and action handler patterns.",
|
|
2937
|
+
value: _copilotkit_shared.A2UI_DEFAULT_DESIGN_GUIDELINES
|
|
2938
|
+
}));
|
|
2939
|
+
return () => {
|
|
2940
|
+
for (const id of ids) copilotkit.removeContext(id);
|
|
2941
|
+
};
|
|
2942
|
+
}, [copilotkit, schemaValue]);
|
|
2943
|
+
return null;
|
|
2035
2944
|
}
|
|
2036
2945
|
|
|
2037
2946
|
//#endregion
|
|
@@ -2134,6 +3043,17 @@ var CopilotKitCoreReact = class extends _copilotkit_core.CopilotKitCore {
|
|
|
2134
3043
|
//#region src/v2/providers/CopilotKitProvider.tsx
|
|
2135
3044
|
const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
|
|
2136
3045
|
const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
|
|
3046
|
+
const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
|
|
3047
|
+
|
|
3048
|
+
- Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
|
|
3049
|
+
- Neutral base palette: white backgrounds, zinc/slate gray text (#09090b for headings, #71717a for secondary text). One accent color for interactive elements.
|
|
3050
|
+
- Use system font stacks (system-ui, -apple-system, sans-serif) at readable sizes (14px body, 600 weight for headings). Tight line-heights.
|
|
3051
|
+
- Small, consistent border-radius (6–8px). Cards and containers use border, not shadow, for separation.
|
|
3052
|
+
- Buttons: solid fill for primary (dark bg, white text), outline for secondary (border + transparent bg). Subtle hover state (slight opacity or background shift).
|
|
3053
|
+
- Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
|
|
3054
|
+
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
|
|
3055
|
+
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
|
|
3056
|
+
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)";
|
|
2137
3057
|
const CopilotKitContext = (0, react.createContext)({
|
|
2138
3058
|
copilotkit: null,
|
|
2139
3059
|
executingToolCallIds: /* @__PURE__ */ new Set()
|
|
@@ -2149,9 +3069,11 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
|
|
|
2149
3069
|
}, [value, warningMessage]);
|
|
2150
3070
|
return value;
|
|
2151
3071
|
}
|
|
2152
|
-
const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, showDevConsole = false, useSingleEndpoint
|
|
3072
|
+
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 }) => {
|
|
2153
3073
|
const [shouldRenderInspector, setShouldRenderInspector] = (0, react.useState)(false);
|
|
2154
3074
|
const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = (0, react.useState)(false);
|
|
3075
|
+
const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = (0, react.useState)(false);
|
|
3076
|
+
const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
|
|
2155
3077
|
const [runtimeLicenseStatus, setRuntimeLicenseStatus] = (0, react.useState)(void 0);
|
|
2156
3078
|
(0, react.useEffect)(() => {
|
|
2157
3079
|
if (typeof window === "undefined") return;
|
|
@@ -2177,9 +3099,22 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2177
3099
|
content: MCPAppsActivityContentSchema,
|
|
2178
3100
|
render: MCPAppsActivityRenderer
|
|
2179
3101
|
}];
|
|
2180
|
-
if (
|
|
3102
|
+
if (openGenUIActive) renderers.push({
|
|
3103
|
+
activityType: OpenGenerativeUIActivityType,
|
|
3104
|
+
content: OpenGenerativeUIContentSchema,
|
|
3105
|
+
render: OpenGenerativeUIActivityRenderer
|
|
3106
|
+
});
|
|
3107
|
+
if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
|
|
3108
|
+
theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme,
|
|
3109
|
+
catalog: a2ui?.catalog,
|
|
3110
|
+
loadingComponent: a2ui?.loadingComponent
|
|
3111
|
+
}));
|
|
2181
3112
|
return renderers;
|
|
2182
|
-
}, [
|
|
3113
|
+
}, [
|
|
3114
|
+
runtimeA2UIEnabled,
|
|
3115
|
+
openGenUIActive,
|
|
3116
|
+
a2ui
|
|
3117
|
+
]);
|
|
2183
3118
|
const allActivityRenderers = (0, react.useMemo)(() => {
|
|
2184
3119
|
return [...renderActivityMessagesList, ...builtInActivityRenderers];
|
|
2185
3120
|
}, [renderActivityMessagesList, builtInActivityRenderers]);
|
|
@@ -2205,6 +3140,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2205
3140
|
const chatApiEndpoint = runtimeUrl ?? (resolvedPublicKey ? COPILOT_CLOUD_CHAT_URL$1 : void 0);
|
|
2206
3141
|
const frontendToolsList = useStableArrayProp(frontendTools, "frontendTools must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.");
|
|
2207
3142
|
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.");
|
|
3143
|
+
const sandboxFunctionsList = useStableArrayProp(openGenerativeUI?.sandboxFunctions, "openGenerativeUI.sandboxFunctions must be a stable array.");
|
|
2208
3144
|
const processedHumanInTheLoopTools = (0, react.useMemo)(() => {
|
|
2209
3145
|
const processedTools = [];
|
|
2210
3146
|
const processedRenderToolCalls = [];
|
|
@@ -2235,15 +3171,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2235
3171
|
renderToolCalls: processedRenderToolCalls
|
|
2236
3172
|
};
|
|
2237
3173
|
}, [humanInTheLoopList]);
|
|
3174
|
+
const builtInFrontendTools = (0, react.useMemo)(() => {
|
|
3175
|
+
if (!openGenUIActive) return [];
|
|
3176
|
+
return [{
|
|
3177
|
+
name: "generateSandboxedUi",
|
|
3178
|
+
description: GENERATE_SANDBOXED_UI_DESCRIPTION,
|
|
3179
|
+
parameters: GenerateSandboxedUiArgsSchema,
|
|
3180
|
+
handler: async () => "UI generated",
|
|
3181
|
+
followUp: true,
|
|
3182
|
+
render: OpenGenerativeUIToolRenderer
|
|
3183
|
+
}];
|
|
3184
|
+
}, [openGenUIActive]);
|
|
2238
3185
|
const allTools = (0, react.useMemo)(() => {
|
|
2239
3186
|
const tools = [];
|
|
2240
3187
|
tools.push(...frontendToolsList);
|
|
3188
|
+
tools.push(...builtInFrontendTools);
|
|
2241
3189
|
tools.push(...processedHumanInTheLoopTools.tools);
|
|
2242
3190
|
return tools;
|
|
2243
|
-
}, [
|
|
3191
|
+
}, [
|
|
3192
|
+
frontendToolsList,
|
|
3193
|
+
builtInFrontendTools,
|
|
3194
|
+
processedHumanInTheLoopTools
|
|
3195
|
+
]);
|
|
2244
3196
|
const allRenderToolCalls = (0, react.useMemo)(() => {
|
|
2245
3197
|
const combined = [...renderToolCallsList];
|
|
2246
|
-
frontendToolsList.forEach((tool) => {
|
|
3198
|
+
[...frontendToolsList, ...builtInFrontendTools].forEach((tool) => {
|
|
2247
3199
|
if (tool.render) {
|
|
2248
3200
|
const args = tool.parameters || (tool.name === "*" ? zod.z.any() : void 0);
|
|
2249
3201
|
if (args) combined.push({
|
|
@@ -2258,25 +3210,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2258
3210
|
}, [
|
|
2259
3211
|
renderToolCallsList,
|
|
2260
3212
|
frontendToolsList,
|
|
3213
|
+
builtInFrontendTools,
|
|
2261
3214
|
processedHumanInTheLoopTools
|
|
2262
3215
|
]);
|
|
2263
3216
|
const copilotkitRef = (0, react.useRef)(null);
|
|
2264
|
-
if (copilotkitRef.current === null)
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
3217
|
+
if (copilotkitRef.current === null) {
|
|
3218
|
+
copilotkitRef.current = new CopilotKitCoreReact({
|
|
3219
|
+
runtimeUrl: chatApiEndpoint,
|
|
3220
|
+
runtimeTransport: useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto",
|
|
3221
|
+
headers: mergedHeaders,
|
|
3222
|
+
credentials,
|
|
3223
|
+
properties,
|
|
3224
|
+
agents__unsafe_dev_only: mergedAgents,
|
|
3225
|
+
tools: allTools,
|
|
3226
|
+
renderToolCalls: allRenderToolCalls,
|
|
3227
|
+
renderActivityMessages: allActivityRenderers,
|
|
3228
|
+
renderCustomMessages: renderCustomMessagesList
|
|
3229
|
+
});
|
|
3230
|
+
if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
|
|
3231
|
+
}
|
|
2276
3232
|
const copilotkit = copilotkitRef.current;
|
|
2277
3233
|
(0, react.useEffect)(() => {
|
|
3234
|
+
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
|
|
2278
3235
|
const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
2279
3236
|
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
|
|
3237
|
+
setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
|
|
2280
3238
|
setRuntimeLicenseStatus(copilotkit.licenseStatus);
|
|
2281
3239
|
} });
|
|
2282
3240
|
return () => {
|
|
@@ -2335,7 +3293,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2335
3293
|
}, [copilotkit]);
|
|
2336
3294
|
(0, react.useEffect)(() => {
|
|
2337
3295
|
copilotkit.setRuntimeUrl(chatApiEndpoint);
|
|
2338
|
-
copilotkit.setRuntimeTransport(useSingleEndpoint ? "single" : "rest");
|
|
3296
|
+
copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
|
|
2339
3297
|
copilotkit.setHeaders(mergedHeaders);
|
|
2340
3298
|
copilotkit.setCredentials(credentials);
|
|
2341
3299
|
copilotkit.setProperties(properties);
|
|
@@ -2369,23 +3327,75 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2369
3327
|
(0, react.useEffect)(() => {
|
|
2370
3328
|
didMountRef.current = true;
|
|
2371
3329
|
}, []);
|
|
3330
|
+
(0, react.useEffect)(() => {
|
|
3331
|
+
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.`);
|
|
3332
|
+
copilotkit.setDefaultThrottleMs(defaultThrottleMs);
|
|
3333
|
+
}, [copilotkit, defaultThrottleMs]);
|
|
3334
|
+
const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
|
|
3335
|
+
(0, react.useLayoutEffect)(() => {
|
|
3336
|
+
if (!copilotkit || !openGenUIActive) return;
|
|
3337
|
+
const id = copilotkit.addContext({
|
|
3338
|
+
description: "Design guidelines for the generateSandboxedUi tool. Follow these when building UI.",
|
|
3339
|
+
value: designSkill
|
|
3340
|
+
});
|
|
3341
|
+
return () => {
|
|
3342
|
+
copilotkit.removeContext(id);
|
|
3343
|
+
};
|
|
3344
|
+
}, [
|
|
3345
|
+
copilotkit,
|
|
3346
|
+
designSkill,
|
|
3347
|
+
openGenUIActive
|
|
3348
|
+
]);
|
|
3349
|
+
const sandboxFunctionsDescriptors = (0, react.useMemo)(() => {
|
|
3350
|
+
if (sandboxFunctionsList.length === 0) return null;
|
|
3351
|
+
return JSON.stringify(sandboxFunctionsList.map((fn) => ({
|
|
3352
|
+
name: fn.name,
|
|
3353
|
+
description: fn.description,
|
|
3354
|
+
parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zod_to_json_schema.zodToJsonSchema })
|
|
3355
|
+
})));
|
|
3356
|
+
}, [sandboxFunctionsList]);
|
|
3357
|
+
(0, react.useLayoutEffect)(() => {
|
|
3358
|
+
if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
|
|
3359
|
+
const id = copilotkit.addContext({
|
|
3360
|
+
description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
|
|
3361
|
+
value: sandboxFunctionsDescriptors
|
|
3362
|
+
});
|
|
3363
|
+
return () => {
|
|
3364
|
+
copilotkit.removeContext(id);
|
|
3365
|
+
};
|
|
3366
|
+
}, [
|
|
3367
|
+
copilotkit,
|
|
3368
|
+
sandboxFunctionsDescriptors,
|
|
3369
|
+
openGenUIActive
|
|
3370
|
+
]);
|
|
2372
3371
|
const contextValue = (0, react.useMemo)(() => ({
|
|
2373
3372
|
copilotkit,
|
|
2374
3373
|
executingToolCallIds
|
|
2375
3374
|
}), [copilotkit, executingToolCallIds]);
|
|
2376
3375
|
const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(null), []);
|
|
2377
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2378
|
-
value:
|
|
2379
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2380
|
-
value:
|
|
2381
|
-
children:
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
3376
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SandboxFunctionsContext.Provider, {
|
|
3377
|
+
value: sandboxFunctionsList,
|
|
3378
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitContext.Provider, {
|
|
3379
|
+
value: contextValue,
|
|
3380
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LicenseContext.Provider, {
|
|
3381
|
+
value: licenseContextValue,
|
|
3382
|
+
children: [
|
|
3383
|
+
runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
|
|
3384
|
+
runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
|
|
3385
|
+
catalog: a2ui?.catalog,
|
|
3386
|
+
includeSchema: a2ui?.includeSchema
|
|
3387
|
+
}),
|
|
3388
|
+
children,
|
|
3389
|
+
shouldRenderInspector ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitInspector, {
|
|
3390
|
+
core: copilotkit,
|
|
3391
|
+
defaultAnchor: inspectorDefaultAnchor
|
|
3392
|
+
}) : null,
|
|
3393
|
+
runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "no_license" }),
|
|
3394
|
+
runtimeLicenseStatus === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expired" }),
|
|
3395
|
+
runtimeLicenseStatus === "invalid" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "invalid" }),
|
|
3396
|
+
runtimeLicenseStatus === "expiring" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expiring" })
|
|
3397
|
+
]
|
|
3398
|
+
})
|
|
2389
3399
|
})
|
|
2390
3400
|
});
|
|
2391
3401
|
};
|
|
@@ -2394,81 +3404,264 @@ const useCopilotKit = () => {
|
|
|
2394
3404
|
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
2395
3405
|
if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
|
|
2396
3406
|
(0, react.useEffect)(() => {
|
|
2397
|
-
const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
2398
|
-
forceUpdate();
|
|
2399
|
-
} });
|
|
3407
|
+
const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
3408
|
+
forceUpdate();
|
|
3409
|
+
} });
|
|
3410
|
+
return () => {
|
|
3411
|
+
subscription.unsubscribe();
|
|
3412
|
+
};
|
|
3413
|
+
}, []);
|
|
3414
|
+
return context;
|
|
3415
|
+
};
|
|
3416
|
+
|
|
3417
|
+
//#endregion
|
|
3418
|
+
//#region src/v2/hooks/use-render-tool-call.tsx
|
|
3419
|
+
/**
|
|
3420
|
+
* Memoized component that renders a single tool call.
|
|
3421
|
+
* This prevents unnecessary re-renders when parent components update
|
|
3422
|
+
* but the tool call data hasn't changed.
|
|
3423
|
+
*/
|
|
3424
|
+
const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
|
|
3425
|
+
const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
|
|
3426
|
+
const toolName = toolCall.function.name;
|
|
3427
|
+
if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
|
|
3428
|
+
name: toolName,
|
|
3429
|
+
args,
|
|
3430
|
+
status: _copilotkit_core.ToolCallStatus.Complete,
|
|
3431
|
+
result: toolMessage.content
|
|
3432
|
+
});
|
|
3433
|
+
else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
|
|
3434
|
+
name: toolName,
|
|
3435
|
+
args,
|
|
3436
|
+
status: _copilotkit_core.ToolCallStatus.Executing,
|
|
3437
|
+
result: void 0
|
|
3438
|
+
});
|
|
3439
|
+
else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
|
|
3440
|
+
name: toolName,
|
|
3441
|
+
args,
|
|
3442
|
+
status: _copilotkit_core.ToolCallStatus.InProgress,
|
|
3443
|
+
result: void 0
|
|
3444
|
+
});
|
|
3445
|
+
}, (prevProps, nextProps) => {
|
|
3446
|
+
if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
|
|
3447
|
+
if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
|
|
3448
|
+
if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
|
|
3449
|
+
if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
|
|
3450
|
+
if (prevProps.isExecuting !== nextProps.isExecuting) return false;
|
|
3451
|
+
if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
|
|
3452
|
+
return true;
|
|
3453
|
+
});
|
|
3454
|
+
/**
|
|
3455
|
+
* Hook that returns a function to render tool calls based on the render functions
|
|
3456
|
+
* defined in CopilotKitProvider.
|
|
3457
|
+
*
|
|
3458
|
+
* @returns A function that takes a tool call and optional tool message and returns the rendered component
|
|
3459
|
+
*/
|
|
3460
|
+
function useRenderToolCall() {
|
|
3461
|
+
const { copilotkit, executingToolCallIds } = useCopilotKit();
|
|
3462
|
+
const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
3463
|
+
const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
|
|
3464
|
+
return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
|
|
3465
|
+
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
3466
|
+
return (0, react.useCallback)(({ toolCall, toolMessage }) => {
|
|
3467
|
+
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
3468
|
+
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
3469
|
+
if (!renderConfig) return null;
|
|
3470
|
+
const RenderComponent = renderConfig.render;
|
|
3471
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
|
|
3472
|
+
toolCall,
|
|
3473
|
+
toolMessage,
|
|
3474
|
+
RenderComponent,
|
|
3475
|
+
isExecuting: executingToolCallIds.has(toolCall.id)
|
|
3476
|
+
}, toolCall.id);
|
|
3477
|
+
}, [
|
|
3478
|
+
renderToolCalls,
|
|
3479
|
+
executingToolCallIds,
|
|
3480
|
+
agentId
|
|
3481
|
+
]);
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
//#endregion
|
|
3485
|
+
//#region src/v2/hooks/use-agent.tsx
|
|
3486
|
+
let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
|
|
3487
|
+
UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
|
|
3488
|
+
UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
|
|
3489
|
+
UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
|
|
3490
|
+
return UseAgentUpdate;
|
|
3491
|
+
}({});
|
|
3492
|
+
const ALL_UPDATES = [
|
|
3493
|
+
UseAgentUpdate.OnMessagesChanged,
|
|
3494
|
+
UseAgentUpdate.OnStateChanged,
|
|
3495
|
+
UseAgentUpdate.OnRunStatusChanged
|
|
3496
|
+
];
|
|
3497
|
+
/**
|
|
3498
|
+
* Clone a registry agent for per-thread isolation.
|
|
3499
|
+
* Copies agent configuration (transport, headers, etc.) but resets conversation
|
|
3500
|
+
* state (messages, threadId, state) so each thread starts fresh.
|
|
3501
|
+
*/
|
|
3502
|
+
function cloneForThread(source, threadId, headers) {
|
|
3503
|
+
const clone = source.clone();
|
|
3504
|
+
if (clone === source) throw new Error(`useAgent: ${source.constructor.name}.clone() returned the same instance. clone() must return a new, independent object.`);
|
|
3505
|
+
clone.threadId = threadId;
|
|
3506
|
+
clone.setMessages([]);
|
|
3507
|
+
clone.setState({});
|
|
3508
|
+
if (clone instanceof _ag_ui_client.HttpAgent) clone.headers = { ...headers };
|
|
3509
|
+
return clone;
|
|
3510
|
+
}
|
|
3511
|
+
/**
|
|
3512
|
+
* Module-level WeakMap: registryAgent → (threadId → clone).
|
|
3513
|
+
* Shared across all useAgent() calls so that every component using the same
|
|
3514
|
+
* (agentId, threadId) pair receives the same agent instance. Using WeakMap
|
|
3515
|
+
* ensures the clone map is garbage-collected when the registry agent is
|
|
3516
|
+
* replaced (e.g. after reconnect or hot-reload).
|
|
3517
|
+
*/
|
|
3518
|
+
const globalThreadCloneMap = /* @__PURE__ */ new WeakMap();
|
|
3519
|
+
/**
|
|
3520
|
+
* Look up an existing per-thread clone without creating one.
|
|
3521
|
+
* Returns undefined when no clone has been created yet for this pair.
|
|
3522
|
+
*/
|
|
3523
|
+
function getThreadClone(registryAgent, threadId) {
|
|
3524
|
+
if (!registryAgent || !threadId) return void 0;
|
|
3525
|
+
return globalThreadCloneMap.get(registryAgent)?.get(threadId);
|
|
3526
|
+
}
|
|
3527
|
+
function getOrCreateThreadClone(existing, threadId, headers) {
|
|
3528
|
+
let byThread = globalThreadCloneMap.get(existing);
|
|
3529
|
+
if (!byThread) {
|
|
3530
|
+
byThread = /* @__PURE__ */ new Map();
|
|
3531
|
+
globalThreadCloneMap.set(existing, byThread);
|
|
3532
|
+
}
|
|
3533
|
+
const cached = byThread.get(threadId);
|
|
3534
|
+
if (cached) return cached;
|
|
3535
|
+
const clone = cloneForThread(existing, threadId, headers);
|
|
3536
|
+
byThread.set(threadId, clone);
|
|
3537
|
+
return clone;
|
|
3538
|
+
}
|
|
3539
|
+
function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
|
|
3540
|
+
agentId ??= _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
3541
|
+
const { copilotkit } = useCopilotKit();
|
|
3542
|
+
const providerThrottleMs = copilotkit.defaultThrottleMs;
|
|
3543
|
+
const chatConfig = useCopilotChatConfiguration();
|
|
3544
|
+
threadId ??= chatConfig?.threadId;
|
|
3545
|
+
const effectiveThrottleMs = (0, react.useMemo)(() => {
|
|
3546
|
+
const resolved = throttleMs ?? providerThrottleMs ?? 0;
|
|
3547
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
3548
|
+
const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
|
|
3549
|
+
console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
|
|
3550
|
+
return 0;
|
|
3551
|
+
}
|
|
3552
|
+
return resolved;
|
|
3553
|
+
}, [throttleMs, providerThrottleMs]);
|
|
3554
|
+
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
3555
|
+
const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
|
|
3556
|
+
const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
3557
|
+
const agent = (0, react.useMemo)(() => {
|
|
3558
|
+
const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
|
|
3559
|
+
const existing = copilotkit.getAgent(agentId);
|
|
3560
|
+
if (existing) {
|
|
3561
|
+
provisionalAgentCache.current.delete(cacheKey);
|
|
3562
|
+
provisionalAgentCache.current.delete(agentId);
|
|
3563
|
+
if (!threadId) return existing;
|
|
3564
|
+
return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
|
|
3565
|
+
}
|
|
3566
|
+
const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
|
|
3567
|
+
const status = copilotkit.runtimeConnectionStatus;
|
|
3568
|
+
if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
3569
|
+
const cached = provisionalAgentCache.current.get(cacheKey);
|
|
3570
|
+
if (cached) {
|
|
3571
|
+
cached.headers = { ...copilotkit.headers };
|
|
3572
|
+
return cached;
|
|
3573
|
+
}
|
|
3574
|
+
const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
|
|
3575
|
+
runtimeUrl: copilotkit.runtimeUrl,
|
|
3576
|
+
agentId,
|
|
3577
|
+
transport: copilotkit.runtimeTransport,
|
|
3578
|
+
runtimeMode: "pending"
|
|
3579
|
+
});
|
|
3580
|
+
provisional.headers = { ...copilotkit.headers };
|
|
3581
|
+
if (threadId) provisional.threadId = threadId;
|
|
3582
|
+
provisionalAgentCache.current.set(cacheKey, provisional);
|
|
3583
|
+
return provisional;
|
|
3584
|
+
}
|
|
3585
|
+
if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
3586
|
+
const cached = provisionalAgentCache.current.get(cacheKey);
|
|
3587
|
+
if (cached) {
|
|
3588
|
+
cached.headers = { ...copilotkit.headers };
|
|
3589
|
+
return cached;
|
|
3590
|
+
}
|
|
3591
|
+
const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
|
|
3592
|
+
runtimeUrl: copilotkit.runtimeUrl,
|
|
3593
|
+
agentId,
|
|
3594
|
+
transport: copilotkit.runtimeTransport,
|
|
3595
|
+
runtimeMode: "pending"
|
|
3596
|
+
});
|
|
3597
|
+
provisional.headers = { ...copilotkit.headers };
|
|
3598
|
+
if (threadId) provisional.threadId = threadId;
|
|
3599
|
+
provisionalAgentCache.current.set(cacheKey, provisional);
|
|
3600
|
+
return provisional;
|
|
3601
|
+
}
|
|
3602
|
+
const knownAgents = Object.keys(copilotkit.agents ?? {});
|
|
3603
|
+
const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
|
|
3604
|
+
throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
|
|
3605
|
+
}, [
|
|
3606
|
+
agentId,
|
|
3607
|
+
threadId,
|
|
3608
|
+
copilotkit.agents,
|
|
3609
|
+
copilotkit.runtimeConnectionStatus,
|
|
3610
|
+
copilotkit.runtimeUrl,
|
|
3611
|
+
copilotkit.runtimeTransport,
|
|
3612
|
+
JSON.stringify(copilotkit.headers)
|
|
3613
|
+
]);
|
|
3614
|
+
(0, react.useEffect)(() => {
|
|
3615
|
+
if (updateFlags.length === 0) return;
|
|
3616
|
+
const handlers = {};
|
|
3617
|
+
let timerId = null;
|
|
3618
|
+
let active = true;
|
|
3619
|
+
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
|
|
3620
|
+
const ms = effectiveThrottleMs;
|
|
3621
|
+
if (ms > 0) {
|
|
3622
|
+
let throttleActive = false;
|
|
3623
|
+
let pending = false;
|
|
3624
|
+
const throttledNotify = () => {
|
|
3625
|
+
if (!active) return;
|
|
3626
|
+
if (!throttleActive) {
|
|
3627
|
+
throttleActive = true;
|
|
3628
|
+
pending = false;
|
|
3629
|
+
forceUpdate();
|
|
3630
|
+
timerId = setTimeout(function trailingEdge() {
|
|
3631
|
+
timerId = null;
|
|
3632
|
+
if (active && pending) {
|
|
3633
|
+
pending = false;
|
|
3634
|
+
forceUpdate();
|
|
3635
|
+
timerId = setTimeout(trailingEdge, ms);
|
|
3636
|
+
} else throttleActive = false;
|
|
3637
|
+
}, ms);
|
|
3638
|
+
} else pending = true;
|
|
3639
|
+
};
|
|
3640
|
+
handlers.onMessagesChanged = throttledNotify;
|
|
3641
|
+
} else handlers.onMessagesChanged = forceUpdate;
|
|
3642
|
+
}
|
|
3643
|
+
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
|
|
3644
|
+
if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
|
|
3645
|
+
handlers.onRunInitialized = forceUpdate;
|
|
3646
|
+
handlers.onRunFinalized = forceUpdate;
|
|
3647
|
+
handlers.onRunFailed = forceUpdate;
|
|
3648
|
+
}
|
|
3649
|
+
const subscription = agent.subscribe(handlers);
|
|
2400
3650
|
return () => {
|
|
3651
|
+
active = false;
|
|
3652
|
+
if (timerId !== null) clearTimeout(timerId);
|
|
2401
3653
|
subscription.unsubscribe();
|
|
2402
3654
|
};
|
|
2403
|
-
}, []);
|
|
2404
|
-
return context;
|
|
2405
|
-
};
|
|
2406
|
-
|
|
2407
|
-
//#endregion
|
|
2408
|
-
//#region src/v2/hooks/use-render-tool-call.tsx
|
|
2409
|
-
/**
|
|
2410
|
-
* Memoized component that renders a single tool call.
|
|
2411
|
-
* This prevents unnecessary re-renders when parent components update
|
|
2412
|
-
* but the tool call data hasn't changed.
|
|
2413
|
-
*/
|
|
2414
|
-
const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
|
|
2415
|
-
const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
|
|
2416
|
-
const toolName = toolCall.function.name;
|
|
2417
|
-
if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
|
|
2418
|
-
name: toolName,
|
|
2419
|
-
args,
|
|
2420
|
-
status: _copilotkit_core.ToolCallStatus.Complete,
|
|
2421
|
-
result: toolMessage.content
|
|
2422
|
-
});
|
|
2423
|
-
else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
|
|
2424
|
-
name: toolName,
|
|
2425
|
-
args,
|
|
2426
|
-
status: _copilotkit_core.ToolCallStatus.Executing,
|
|
2427
|
-
result: void 0
|
|
2428
|
-
});
|
|
2429
|
-
else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
|
|
2430
|
-
name: toolName,
|
|
2431
|
-
args,
|
|
2432
|
-
status: _copilotkit_core.ToolCallStatus.InProgress,
|
|
2433
|
-
result: void 0
|
|
2434
|
-
});
|
|
2435
|
-
}, (prevProps, nextProps) => {
|
|
2436
|
-
if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
|
|
2437
|
-
if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
|
|
2438
|
-
if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
|
|
2439
|
-
if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
|
|
2440
|
-
if (prevProps.isExecuting !== nextProps.isExecuting) return false;
|
|
2441
|
-
if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
|
|
2442
|
-
return true;
|
|
2443
|
-
});
|
|
2444
|
-
/**
|
|
2445
|
-
* Hook that returns a function to render tool calls based on the render functions
|
|
2446
|
-
* defined in CopilotKitProvider.
|
|
2447
|
-
*
|
|
2448
|
-
* @returns A function that takes a tool call and optional tool message and returns the rendered component
|
|
2449
|
-
*/
|
|
2450
|
-
function useRenderToolCall() {
|
|
2451
|
-
const { copilotkit, executingToolCallIds } = useCopilotKit();
|
|
2452
|
-
const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
2453
|
-
const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
|
|
2454
|
-
return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
|
|
2455
|
-
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
2456
|
-
return (0, react.useCallback)(({ toolCall, toolMessage }) => {
|
|
2457
|
-
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
2458
|
-
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
2459
|
-
if (!renderConfig) return null;
|
|
2460
|
-
const RenderComponent = renderConfig.render;
|
|
2461
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
|
|
2462
|
-
toolCall,
|
|
2463
|
-
toolMessage,
|
|
2464
|
-
RenderComponent,
|
|
2465
|
-
isExecuting: executingToolCallIds.has(toolCall.id)
|
|
2466
|
-
}, toolCall.id);
|
|
2467
3655
|
}, [
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
3656
|
+
agent,
|
|
3657
|
+
forceUpdate,
|
|
3658
|
+
effectiveThrottleMs,
|
|
3659
|
+
updateFlags
|
|
2471
3660
|
]);
|
|
3661
|
+
(0, react.useEffect)(() => {
|
|
3662
|
+
if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...copilotkit.headers };
|
|
3663
|
+
}, [agent, JSON.stringify(copilotkit.headers)]);
|
|
3664
|
+
return { agent };
|
|
2472
3665
|
}
|
|
2473
3666
|
|
|
2474
3667
|
//#endregion
|
|
@@ -2488,7 +3681,8 @@ function useRenderCustomMessages() {
|
|
|
2488
3681
|
const { message, position } = params;
|
|
2489
3682
|
const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
|
|
2490
3683
|
const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
|
|
2491
|
-
const
|
|
3684
|
+
const registryAgent = copilotkit.getAgent(agentId);
|
|
3685
|
+
const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
|
|
2492
3686
|
if (!agent) throw new Error("Agent not found");
|
|
2493
3687
|
const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
|
|
2494
3688
|
const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
|
|
@@ -2520,7 +3714,8 @@ function useRenderCustomMessages() {
|
|
|
2520
3714
|
//#region src/v2/hooks/use-render-activity-message.tsx
|
|
2521
3715
|
function useRenderActivityMessage() {
|
|
2522
3716
|
const { copilotkit } = useCopilotKit();
|
|
2523
|
-
const
|
|
3717
|
+
const config = useCopilotChatConfiguration();
|
|
3718
|
+
const agentId = config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
2524
3719
|
const renderers = copilotkit.renderActivityMessages;
|
|
2525
3720
|
const findRenderer = (0, react.useCallback)((activityType) => {
|
|
2526
3721
|
if (!renderers.length) return null;
|
|
@@ -2536,7 +3731,8 @@ function useRenderActivityMessage() {
|
|
|
2536
3731
|
return null;
|
|
2537
3732
|
}
|
|
2538
3733
|
const Component = renderer.render;
|
|
2539
|
-
const
|
|
3734
|
+
const registryAgent = copilotkit.getAgent(agentId);
|
|
3735
|
+
const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
|
|
2540
3736
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
2541
3737
|
activityType: message.activityType,
|
|
2542
3738
|
content: parseResult.data,
|
|
@@ -2545,6 +3741,7 @@ function useRenderActivityMessage() {
|
|
|
2545
3741
|
}, message.id);
|
|
2546
3742
|
}, [
|
|
2547
3743
|
agentId,
|
|
3744
|
+
config?.threadId,
|
|
2548
3745
|
copilotkit,
|
|
2549
3746
|
findRenderer
|
|
2550
3747
|
]);
|
|
@@ -2570,7 +3767,7 @@ function useFrontendTool(tool, deps) {
|
|
|
2570
3767
|
copilotkit.removeTool(name, tool.agentId);
|
|
2571
3768
|
}
|
|
2572
3769
|
copilotkit.addTool(tool);
|
|
2573
|
-
if (tool.render
|
|
3770
|
+
if (tool.render) copilotkit.addHookRenderToolCall({
|
|
2574
3771
|
name,
|
|
2575
3772
|
args: tool.parameters,
|
|
2576
3773
|
agentId: tool.agentId,
|
|
@@ -2655,18 +3852,6 @@ function useComponent(config, deps) {
|
|
|
2655
3852
|
}, deps);
|
|
2656
3853
|
}
|
|
2657
3854
|
|
|
2658
|
-
//#endregion
|
|
2659
|
-
//#region src/v2/types/defineToolCallRenderer.ts
|
|
2660
|
-
function defineToolCallRenderer(def) {
|
|
2661
|
-
const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
|
|
2662
|
-
return {
|
|
2663
|
-
name: def.name,
|
|
2664
|
-
args: argsSchema,
|
|
2665
|
-
render: def.render,
|
|
2666
|
-
...def.agentId ? { agentId: def.agentId } : {}
|
|
2667
|
-
};
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
3855
|
//#endregion
|
|
2671
3856
|
//#region src/v2/hooks/use-render-tool.tsx
|
|
2672
3857
|
const EMPTY_DEPS = [];
|
|
@@ -2920,190 +4105,79 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
|
|
|
2920
4105
|
fontSize: "11px",
|
|
2921
4106
|
lineHeight: 1.6,
|
|
2922
4107
|
color: "#27272a",
|
|
2923
|
-
whiteSpace: "pre-wrap",
|
|
2924
|
-
wordBreak: "break-word"
|
|
2925
|
-
},
|
|
2926
|
-
children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
2927
|
-
})] })]
|
|
2928
|
-
})]
|
|
2929
|
-
})
|
|
2930
|
-
});
|
|
2931
|
-
}
|
|
2932
|
-
|
|
2933
|
-
//#endregion
|
|
2934
|
-
//#region src/v2/hooks/use-human-in-the-loop.tsx
|
|
2935
|
-
function useHumanInTheLoop(tool, deps) {
|
|
2936
|
-
const { copilotkit } = useCopilotKit();
|
|
2937
|
-
const resolvePromiseRef = (0, react.useRef)(null);
|
|
2938
|
-
const respond = (0, react.useCallback)(async (result) => {
|
|
2939
|
-
if (resolvePromiseRef.current) {
|
|
2940
|
-
resolvePromiseRef.current(result);
|
|
2941
|
-
resolvePromiseRef.current = null;
|
|
2942
|
-
}
|
|
2943
|
-
}, []);
|
|
2944
|
-
const handler = (0, react.useCallback)(async () => {
|
|
2945
|
-
return new Promise((resolve) => {
|
|
2946
|
-
resolvePromiseRef.current = resolve;
|
|
2947
|
-
});
|
|
2948
|
-
}, []);
|
|
2949
|
-
const RenderComponent = (0, react.useCallback)((props) => {
|
|
2950
|
-
const ToolComponent = tool.render;
|
|
2951
|
-
if (props.status === "inProgress") {
|
|
2952
|
-
const enhancedProps = {
|
|
2953
|
-
...props,
|
|
2954
|
-
name: tool.name,
|
|
2955
|
-
description: tool.description || "",
|
|
2956
|
-
respond: void 0
|
|
2957
|
-
};
|
|
2958
|
-
return react.default.createElement(ToolComponent, enhancedProps);
|
|
2959
|
-
} else if (props.status === "executing") {
|
|
2960
|
-
const enhancedProps = {
|
|
2961
|
-
...props,
|
|
2962
|
-
name: tool.name,
|
|
2963
|
-
description: tool.description || "",
|
|
2964
|
-
respond
|
|
2965
|
-
};
|
|
2966
|
-
return react.default.createElement(ToolComponent, enhancedProps);
|
|
2967
|
-
} else if (props.status === "complete") {
|
|
2968
|
-
const enhancedProps = {
|
|
2969
|
-
...props,
|
|
2970
|
-
name: tool.name,
|
|
2971
|
-
description: tool.description || "",
|
|
2972
|
-
respond: void 0
|
|
2973
|
-
};
|
|
2974
|
-
return react.default.createElement(ToolComponent, enhancedProps);
|
|
2975
|
-
}
|
|
2976
|
-
return react.default.createElement(ToolComponent, props);
|
|
2977
|
-
}, [
|
|
2978
|
-
tool.render,
|
|
2979
|
-
tool.name,
|
|
2980
|
-
tool.description,
|
|
2981
|
-
respond
|
|
2982
|
-
]);
|
|
2983
|
-
useFrontendTool({
|
|
2984
|
-
...tool,
|
|
2985
|
-
handler,
|
|
2986
|
-
render: RenderComponent
|
|
2987
|
-
}, deps);
|
|
2988
|
-
(0, react.useEffect)(() => {
|
|
2989
|
-
return () => {
|
|
2990
|
-
copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
|
|
2991
|
-
};
|
|
2992
|
-
}, [
|
|
2993
|
-
copilotkit,
|
|
2994
|
-
tool.name,
|
|
2995
|
-
tool.agentId
|
|
2996
|
-
]);
|
|
2997
|
-
}
|
|
2998
|
-
|
|
2999
|
-
//#endregion
|
|
3000
|
-
//#region src/v2/hooks/use-agent.tsx
|
|
3001
|
-
let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
|
|
3002
|
-
UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
|
|
3003
|
-
UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
|
|
3004
|
-
UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
|
|
3005
|
-
return UseAgentUpdate;
|
|
3006
|
-
}({});
|
|
3007
|
-
const ALL_UPDATES = [
|
|
3008
|
-
UseAgentUpdate.OnMessagesChanged,
|
|
3009
|
-
UseAgentUpdate.OnStateChanged,
|
|
3010
|
-
UseAgentUpdate.OnRunStatusChanged
|
|
3011
|
-
];
|
|
3012
|
-
function useAgent({ agentId, updates } = {}) {
|
|
3013
|
-
agentId ??= _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
3014
|
-
const { copilotkit } = useCopilotKit();
|
|
3015
|
-
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
3016
|
-
const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
|
|
3017
|
-
const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
3018
|
-
const agent = (0, react.useMemo)(() => {
|
|
3019
|
-
const existing = copilotkit.getAgent(agentId);
|
|
3020
|
-
if (existing) {
|
|
3021
|
-
provisionalAgentCache.current.delete(agentId);
|
|
3022
|
-
return existing;
|
|
3023
|
-
}
|
|
3024
|
-
const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
|
|
3025
|
-
const status = copilotkit.runtimeConnectionStatus;
|
|
3026
|
-
if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
3027
|
-
const cached = provisionalAgentCache.current.get(agentId);
|
|
3028
|
-
if (cached) {
|
|
3029
|
-
cached.headers = { ...copilotkit.headers };
|
|
3030
|
-
return cached;
|
|
3031
|
-
}
|
|
3032
|
-
const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
|
|
3033
|
-
runtimeUrl: copilotkit.runtimeUrl,
|
|
3034
|
-
agentId,
|
|
3035
|
-
transport: copilotkit.runtimeTransport,
|
|
3036
|
-
runtimeMode: "pending"
|
|
3037
|
-
});
|
|
3038
|
-
provisional.headers = { ...copilotkit.headers };
|
|
3039
|
-
provisionalAgentCache.current.set(agentId, provisional);
|
|
3040
|
-
return provisional;
|
|
3041
|
-
}
|
|
3042
|
-
if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
3043
|
-
const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
|
|
3044
|
-
runtimeUrl: copilotkit.runtimeUrl,
|
|
3045
|
-
agentId,
|
|
3046
|
-
transport: copilotkit.runtimeTransport,
|
|
3047
|
-
runtimeMode: "pending"
|
|
3048
|
-
});
|
|
3049
|
-
provisional.headers = { ...copilotkit.headers };
|
|
3050
|
-
return provisional;
|
|
3051
|
-
}
|
|
3052
|
-
const knownAgents = Object.keys(copilotkit.agents ?? {});
|
|
3053
|
-
const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
|
|
3054
|
-
throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
|
|
3055
|
-
}, [
|
|
3056
|
-
agentId,
|
|
3057
|
-
copilotkit.agents,
|
|
3058
|
-
copilotkit.runtimeConnectionStatus,
|
|
3059
|
-
copilotkit.runtimeUrl,
|
|
3060
|
-
copilotkit.runtimeTransport,
|
|
3061
|
-
JSON.stringify(copilotkit.headers)
|
|
3062
|
-
]);
|
|
3063
|
-
(0, react.useEffect)(() => {
|
|
3064
|
-
if (updateFlags.length === 0) return;
|
|
3065
|
-
const handlers = {};
|
|
3066
|
-
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = () => {
|
|
3067
|
-
forceUpdate();
|
|
3068
|
-
};
|
|
3069
|
-
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
|
|
3070
|
-
if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
|
|
3071
|
-
handlers.onRunInitialized = forceUpdate;
|
|
3072
|
-
handlers.onRunFinalized = forceUpdate;
|
|
3073
|
-
handlers.onRunFailed = forceUpdate;
|
|
3074
|
-
}
|
|
3075
|
-
const subscription = agent.subscribe(handlers);
|
|
3076
|
-
return () => subscription.unsubscribe();
|
|
3077
|
-
}, [
|
|
3078
|
-
agent,
|
|
3079
|
-
forceUpdate,
|
|
3080
|
-
JSON.stringify(updateFlags)
|
|
3081
|
-
]);
|
|
3082
|
-
return { agent };
|
|
4108
|
+
whiteSpace: "pre-wrap",
|
|
4109
|
+
wordBreak: "break-word"
|
|
4110
|
+
},
|
|
4111
|
+
children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
4112
|
+
})] })]
|
|
4113
|
+
})]
|
|
4114
|
+
})
|
|
4115
|
+
});
|
|
3083
4116
|
}
|
|
3084
4117
|
|
|
3085
4118
|
//#endregion
|
|
3086
|
-
//#region src/v2/hooks/use-
|
|
3087
|
-
function
|
|
3088
|
-
const { description, value } = context;
|
|
4119
|
+
//#region src/v2/hooks/use-human-in-the-loop.tsx
|
|
4120
|
+
function useHumanInTheLoop(tool, deps) {
|
|
3089
4121
|
const { copilotkit } = useCopilotKit();
|
|
3090
|
-
const
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
4122
|
+
const resolvePromiseRef = (0, react.useRef)(null);
|
|
4123
|
+
const respond = (0, react.useCallback)(async (result) => {
|
|
4124
|
+
if (resolvePromiseRef.current) {
|
|
4125
|
+
resolvePromiseRef.current(result);
|
|
4126
|
+
resolvePromiseRef.current = null;
|
|
4127
|
+
}
|
|
4128
|
+
}, []);
|
|
4129
|
+
const handler = (0, react.useCallback)(async () => {
|
|
4130
|
+
return new Promise((resolve) => {
|
|
4131
|
+
resolvePromiseRef.current = resolve;
|
|
3099
4132
|
});
|
|
4133
|
+
}, []);
|
|
4134
|
+
const RenderComponent = (0, react.useCallback)((props) => {
|
|
4135
|
+
const ToolComponent = tool.render;
|
|
4136
|
+
if (props.status === "inProgress") {
|
|
4137
|
+
const enhancedProps = {
|
|
4138
|
+
...props,
|
|
4139
|
+
name: tool.name,
|
|
4140
|
+
description: tool.description || "",
|
|
4141
|
+
respond: void 0
|
|
4142
|
+
};
|
|
4143
|
+
return react.default.createElement(ToolComponent, enhancedProps);
|
|
4144
|
+
} else if (props.status === "executing") {
|
|
4145
|
+
const enhancedProps = {
|
|
4146
|
+
...props,
|
|
4147
|
+
name: tool.name,
|
|
4148
|
+
description: tool.description || "",
|
|
4149
|
+
respond
|
|
4150
|
+
};
|
|
4151
|
+
return react.default.createElement(ToolComponent, enhancedProps);
|
|
4152
|
+
} else if (props.status === "complete") {
|
|
4153
|
+
const enhancedProps = {
|
|
4154
|
+
...props,
|
|
4155
|
+
name: tool.name,
|
|
4156
|
+
description: tool.description || "",
|
|
4157
|
+
respond: void 0
|
|
4158
|
+
};
|
|
4159
|
+
return react.default.createElement(ToolComponent, enhancedProps);
|
|
4160
|
+
}
|
|
4161
|
+
return react.default.createElement(ToolComponent, props);
|
|
4162
|
+
}, [
|
|
4163
|
+
tool.render,
|
|
4164
|
+
tool.name,
|
|
4165
|
+
tool.description,
|
|
4166
|
+
respond
|
|
4167
|
+
]);
|
|
4168
|
+
useFrontendTool({
|
|
4169
|
+
...tool,
|
|
4170
|
+
handler,
|
|
4171
|
+
render: RenderComponent
|
|
4172
|
+
}, deps);
|
|
4173
|
+
(0, react.useEffect)(() => {
|
|
3100
4174
|
return () => {
|
|
3101
|
-
copilotkit.
|
|
4175
|
+
copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
|
|
3102
4176
|
};
|
|
3103
4177
|
}, [
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
4178
|
+
copilotkit,
|
|
4179
|
+
tool.name,
|
|
4180
|
+
tool.agentId
|
|
3107
4181
|
]);
|
|
3108
4182
|
}
|
|
3109
4183
|
|
|
@@ -3516,11 +4590,19 @@ function useThreadStoreSelector(store, selector) {
|
|
|
3516
4590
|
function useThreads$1({ agentId, includeArchived, limit }) {
|
|
3517
4591
|
const { copilotkit } = useCopilotKit();
|
|
3518
4592
|
const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
|
|
3519
|
-
const
|
|
4593
|
+
const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
|
|
4594
|
+
const threads = (0, react.useMemo)(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
|
|
4595
|
+
id,
|
|
4596
|
+
agentId,
|
|
4597
|
+
name,
|
|
4598
|
+
archived,
|
|
4599
|
+
createdAt,
|
|
4600
|
+
updatedAt
|
|
4601
|
+
})), [coreThreads]);
|
|
3520
4602
|
const storeIsLoading = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsIsLoading);
|
|
3521
4603
|
const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
|
|
3522
|
-
const
|
|
3523
|
-
const
|
|
4604
|
+
const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
|
|
4605
|
+
const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
|
|
3524
4606
|
const headersKey = (0, react.useMemo)(() => {
|
|
3525
4607
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
3526
4608
|
}, [copilotkit.headers]);
|
|
@@ -3563,15 +4645,173 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
3563
4645
|
threads,
|
|
3564
4646
|
isLoading,
|
|
3565
4647
|
error,
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
4648
|
+
hasMoreThreads,
|
|
4649
|
+
isFetchingMoreThreads,
|
|
4650
|
+
fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
|
|
3569
4651
|
renameThread,
|
|
3570
4652
|
archiveThread,
|
|
3571
4653
|
deleteThread
|
|
3572
4654
|
};
|
|
3573
4655
|
}
|
|
3574
4656
|
|
|
4657
|
+
//#endregion
|
|
4658
|
+
//#region src/v2/hooks/use-attachments.tsx
|
|
4659
|
+
/**
|
|
4660
|
+
* Hook that manages file attachment state — uploads, drag-and-drop, paste,
|
|
4661
|
+
* and lifecycle. All returned callbacks are referentially stable across
|
|
4662
|
+
* renders (via useCallback) to avoid destabilizing downstream memoization.
|
|
4663
|
+
*/
|
|
4664
|
+
function useAttachments({ config }) {
|
|
4665
|
+
const enabled = config?.enabled ?? false;
|
|
4666
|
+
const [attachments, setAttachments] = (0, react.useState)([]);
|
|
4667
|
+
const [dragOver, setDragOver] = (0, react.useState)(false);
|
|
4668
|
+
const fileInputRef = (0, react.useRef)(null);
|
|
4669
|
+
const containerRef = (0, react.useRef)(null);
|
|
4670
|
+
const configRef = (0, react.useRef)(config);
|
|
4671
|
+
configRef.current = config;
|
|
4672
|
+
const attachmentsRef = (0, react.useRef)([]);
|
|
4673
|
+
attachmentsRef.current = attachments;
|
|
4674
|
+
const processFiles = (0, react.useCallback)(async (files) => {
|
|
4675
|
+
const cfg = configRef.current;
|
|
4676
|
+
const accept = cfg?.accept ?? "*/*";
|
|
4677
|
+
const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
|
|
4678
|
+
const rejectedFiles = files.filter((file) => !(0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
|
|
4679
|
+
for (const file of rejectedFiles) cfg?.onUploadFailed?.({
|
|
4680
|
+
reason: "invalid-type",
|
|
4681
|
+
file,
|
|
4682
|
+
message: `File "${file.name}" is not accepted. Supported types: ${accept}`
|
|
4683
|
+
});
|
|
4684
|
+
const validFiles = files.filter((file) => (0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
|
|
4685
|
+
for (const file of validFiles) {
|
|
4686
|
+
if ((0, _copilotkit_shared.exceedsMaxSize)(file, maxSize)) {
|
|
4687
|
+
cfg?.onUploadFailed?.({
|
|
4688
|
+
reason: "file-too-large",
|
|
4689
|
+
file,
|
|
4690
|
+
message: `File "${file.name}" exceeds the maximum size of ${(0, _copilotkit_shared.formatFileSize)(maxSize)}`
|
|
4691
|
+
});
|
|
4692
|
+
continue;
|
|
4693
|
+
}
|
|
4694
|
+
const modality = (0, _copilotkit_shared.getModalityFromMimeType)(file.type);
|
|
4695
|
+
const placeholderId = (0, _copilotkit_shared.randomUUID)();
|
|
4696
|
+
const placeholder = {
|
|
4697
|
+
id: placeholderId,
|
|
4698
|
+
type: modality,
|
|
4699
|
+
source: {
|
|
4700
|
+
type: "data",
|
|
4701
|
+
value: "",
|
|
4702
|
+
mimeType: file.type
|
|
4703
|
+
},
|
|
4704
|
+
filename: file.name,
|
|
4705
|
+
size: file.size,
|
|
4706
|
+
status: "uploading"
|
|
4707
|
+
};
|
|
4708
|
+
setAttachments((prev) => [...prev, placeholder]);
|
|
4709
|
+
try {
|
|
4710
|
+
let source;
|
|
4711
|
+
let uploadMetadata;
|
|
4712
|
+
if (cfg?.onUpload) {
|
|
4713
|
+
const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
|
|
4714
|
+
source = uploadSource;
|
|
4715
|
+
uploadMetadata = meta;
|
|
4716
|
+
} else source = {
|
|
4717
|
+
type: "data",
|
|
4718
|
+
value: await (0, _copilotkit_shared.readFileAsBase64)(file),
|
|
4719
|
+
mimeType: file.type
|
|
4720
|
+
};
|
|
4721
|
+
let thumbnail;
|
|
4722
|
+
if (modality === "video") thumbnail = await (0, _copilotkit_shared.generateVideoThumbnail)(file);
|
|
4723
|
+
setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
|
|
4724
|
+
...att,
|
|
4725
|
+
source,
|
|
4726
|
+
status: "ready",
|
|
4727
|
+
thumbnail,
|
|
4728
|
+
metadata: uploadMetadata
|
|
4729
|
+
} : att));
|
|
4730
|
+
} catch (error) {
|
|
4731
|
+
setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
|
|
4732
|
+
console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
|
|
4733
|
+
cfg?.onUploadFailed?.({
|
|
4734
|
+
reason: "upload-failed",
|
|
4735
|
+
file,
|
|
4736
|
+
message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
|
|
4737
|
+
});
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4740
|
+
}, []);
|
|
4741
|
+
const handleFileUpload = (0, react.useCallback)(async (e) => {
|
|
4742
|
+
if (!e.target.files?.length) return;
|
|
4743
|
+
try {
|
|
4744
|
+
await processFiles(Array.from(e.target.files));
|
|
4745
|
+
} catch (error) {
|
|
4746
|
+
console.error("[CopilotKit] Upload error:", error);
|
|
4747
|
+
}
|
|
4748
|
+
}, [processFiles]);
|
|
4749
|
+
const handleDragOver = (0, react.useCallback)((e) => {
|
|
4750
|
+
if (!configRef.current?.enabled) return;
|
|
4751
|
+
e.preventDefault();
|
|
4752
|
+
e.stopPropagation();
|
|
4753
|
+
setDragOver(true);
|
|
4754
|
+
}, []);
|
|
4755
|
+
const handleDragLeave = (0, react.useCallback)((e) => {
|
|
4756
|
+
e.preventDefault();
|
|
4757
|
+
e.stopPropagation();
|
|
4758
|
+
setDragOver(false);
|
|
4759
|
+
}, []);
|
|
4760
|
+
const handleDrop = (0, react.useCallback)(async (e) => {
|
|
4761
|
+
e.preventDefault();
|
|
4762
|
+
e.stopPropagation();
|
|
4763
|
+
setDragOver(false);
|
|
4764
|
+
if (!configRef.current?.enabled) return;
|
|
4765
|
+
const files = Array.from(e.dataTransfer.files);
|
|
4766
|
+
if (files.length > 0) try {
|
|
4767
|
+
await processFiles(files);
|
|
4768
|
+
} catch (error) {
|
|
4769
|
+
console.error("[CopilotKit] Drop error:", error);
|
|
4770
|
+
}
|
|
4771
|
+
}, [processFiles]);
|
|
4772
|
+
(0, react.useEffect)(() => {
|
|
4773
|
+
if (!enabled) return;
|
|
4774
|
+
const handlePaste = async (e) => {
|
|
4775
|
+
const target = e.target;
|
|
4776
|
+
if (!target || !containerRef.current?.contains(target)) return;
|
|
4777
|
+
const accept = configRef.current?.accept ?? "*/*";
|
|
4778
|
+
const fileItems = Array.from(e.clipboardData?.items || []).filter((item) => item.kind === "file" && item.getAsFile() !== null && (0, _copilotkit_shared.matchesAcceptFilter)(item.getAsFile(), accept));
|
|
4779
|
+
if (fileItems.length === 0) return;
|
|
4780
|
+
e.preventDefault();
|
|
4781
|
+
const files = fileItems.map((item) => item.getAsFile()).filter((f) => f !== null);
|
|
4782
|
+
try {
|
|
4783
|
+
await processFiles(files);
|
|
4784
|
+
} catch (error) {
|
|
4785
|
+
console.error("[CopilotKit] Paste error:", error);
|
|
4786
|
+
}
|
|
4787
|
+
};
|
|
4788
|
+
document.addEventListener("paste", handlePaste);
|
|
4789
|
+
return () => document.removeEventListener("paste", handlePaste);
|
|
4790
|
+
}, [enabled, processFiles]);
|
|
4791
|
+
return {
|
|
4792
|
+
attachments,
|
|
4793
|
+
enabled,
|
|
4794
|
+
dragOver,
|
|
4795
|
+
fileInputRef,
|
|
4796
|
+
containerRef,
|
|
4797
|
+
processFiles,
|
|
4798
|
+
handleFileUpload,
|
|
4799
|
+
handleDragOver,
|
|
4800
|
+
handleDragLeave,
|
|
4801
|
+
handleDrop,
|
|
4802
|
+
removeAttachment: (0, react.useCallback)((id) => {
|
|
4803
|
+
setAttachments((prev) => prev.filter((a) => a.id !== id));
|
|
4804
|
+
}, []),
|
|
4805
|
+
consumeAttachments: (0, react.useCallback)(() => {
|
|
4806
|
+
const ready = attachmentsRef.current.filter((a) => a.status === "ready");
|
|
4807
|
+
if (ready.length === 0) return ready;
|
|
4808
|
+
setAttachments((prev) => prev.filter((a) => a.status !== "ready"));
|
|
4809
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
4810
|
+
return ready;
|
|
4811
|
+
}, [])
|
|
4812
|
+
};
|
|
4813
|
+
}
|
|
4814
|
+
|
|
3575
4815
|
//#endregion
|
|
3576
4816
|
//#region src/v2/components/chat/CopilotChatToolCallsView.tsx
|
|
3577
4817
|
function CopilotChatToolCallsView({ message, messages = [] }) {
|
|
@@ -3688,9 +4928,19 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
|
|
|
3688
4928
|
_CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
|
|
3689
4929
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
3690
4930
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
4931
|
+
const timerRef = (0, react.useRef)(null);
|
|
4932
|
+
(0, react.useEffect)(() => {
|
|
4933
|
+
return () => {
|
|
4934
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
4935
|
+
};
|
|
4936
|
+
}, []);
|
|
3691
4937
|
const handleClick = (event) => {
|
|
3692
4938
|
setCopied(true);
|
|
3693
|
-
|
|
4939
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
4940
|
+
timerRef.current = setTimeout(() => {
|
|
4941
|
+
timerRef.current = null;
|
|
4942
|
+
setCopied(false);
|
|
4943
|
+
}, 2e3);
|
|
3694
4944
|
if (onClick) onClick(event);
|
|
3695
4945
|
};
|
|
3696
4946
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
|
|
@@ -3748,6 +4998,79 @@ CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantM
|
|
|
3748
4998
|
CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
|
|
3749
4999
|
var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
|
|
3750
5000
|
|
|
5001
|
+
//#endregion
|
|
5002
|
+
//#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
|
|
5003
|
+
const ImageAttachment = (0, react.memo)(function ImageAttachment({ src, className }) {
|
|
5004
|
+
const [error, setError] = (0, react.useState)(false);
|
|
5005
|
+
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5006
|
+
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),
|
|
5007
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Failed to load image" })
|
|
5008
|
+
});
|
|
5009
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5010
|
+
src,
|
|
5011
|
+
alt: "Image attachment",
|
|
5012
|
+
className: cn("cpk:max-w-full cpk:h-auto cpk:rounded-lg", className),
|
|
5013
|
+
onError: () => setError(true)
|
|
5014
|
+
});
|
|
5015
|
+
});
|
|
5016
|
+
const AudioAttachment = (0, react.memo)(function AudioAttachment({ src, filename, className }) {
|
|
5017
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5018
|
+
className: cn("cpk:flex cpk:flex-col cpk:gap-1", className),
|
|
5019
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
|
|
5020
|
+
src,
|
|
5021
|
+
controls: true,
|
|
5022
|
+
preload: "metadata",
|
|
5023
|
+
className: "cpk:max-w-[300px] cpk:w-full cpk:h-10"
|
|
5024
|
+
}), filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5025
|
+
className: "cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]",
|
|
5026
|
+
children: filename
|
|
5027
|
+
})]
|
|
5028
|
+
});
|
|
5029
|
+
});
|
|
5030
|
+
const VideoAttachment = (0, react.memo)(function VideoAttachment({ src, className }) {
|
|
5031
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
|
|
5032
|
+
src,
|
|
5033
|
+
controls: true,
|
|
5034
|
+
preload: "metadata",
|
|
5035
|
+
className: cn("cpk:max-w-[400px] cpk:w-full cpk:rounded-lg", className)
|
|
5036
|
+
});
|
|
5037
|
+
});
|
|
5038
|
+
const DocumentAttachment = (0, react.memo)(function DocumentAttachment({ source, filename, className }) {
|
|
5039
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5040
|
+
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),
|
|
5041
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5042
|
+
className: "cpk:text-xs cpk:font-bold cpk:uppercase",
|
|
5043
|
+
children: (0, _copilotkit_shared.getDocumentIcon)(source.mimeType ?? "")
|
|
5044
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5045
|
+
className: "cpk:text-sm cpk:text-muted-foreground cpk:truncate",
|
|
5046
|
+
children: filename || source.mimeType || "Unknown type"
|
|
5047
|
+
})]
|
|
5048
|
+
});
|
|
5049
|
+
});
|
|
5050
|
+
const CopilotChatAttachmentRenderer = ({ type, source, filename, className }) => {
|
|
5051
|
+
const src = (0, _copilotkit_shared.getSourceUrl)(source);
|
|
5052
|
+
switch (type) {
|
|
5053
|
+
case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageAttachment, {
|
|
5054
|
+
src,
|
|
5055
|
+
className
|
|
5056
|
+
});
|
|
5057
|
+
case "audio": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AudioAttachment, {
|
|
5058
|
+
src,
|
|
5059
|
+
filename,
|
|
5060
|
+
className
|
|
5061
|
+
});
|
|
5062
|
+
case "video": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoAttachment, {
|
|
5063
|
+
src,
|
|
5064
|
+
className
|
|
5065
|
+
});
|
|
5066
|
+
case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentAttachment, {
|
|
5067
|
+
source,
|
|
5068
|
+
filename,
|
|
5069
|
+
className
|
|
5070
|
+
});
|
|
5071
|
+
}
|
|
5072
|
+
};
|
|
5073
|
+
|
|
3751
5074
|
//#endregion
|
|
3752
5075
|
//#region src/v2/components/chat/CopilotChatUserMessage.tsx
|
|
3753
5076
|
function flattenUserMessageContent(content) {
|
|
@@ -3758,8 +5081,17 @@ function flattenUserMessageContent(content) {
|
|
|
3758
5081
|
return "";
|
|
3759
5082
|
}).filter((text) => text.length > 0).join("\n");
|
|
3760
5083
|
}
|
|
5084
|
+
function getMediaParts(content) {
|
|
5085
|
+
if (!content || typeof content === "string") return [];
|
|
5086
|
+
return content.filter((part) => part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document");
|
|
5087
|
+
}
|
|
5088
|
+
function getFilename(part) {
|
|
5089
|
+
const meta = part.metadata;
|
|
5090
|
+
if (meta != null && typeof meta === "object" && "filename" in meta && typeof meta.filename === "string") return meta.filename;
|
|
5091
|
+
}
|
|
3761
5092
|
function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfBranches, onSwitchToBranch, additionalToolbarItems, messageRenderer, toolbar, copyButton, editButton, branchNavigation, children, className, ...props }) {
|
|
3762
5093
|
const flattenedContent = (0, react.useMemo)(() => flattenUserMessageContent(message.content), [message.content]);
|
|
5094
|
+
const mediaParts = (0, react.useMemo)(() => getMediaParts(message.content), [message.content]);
|
|
3763
5095
|
const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
|
|
3764
5096
|
const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
|
|
3765
5097
|
if (flattenedContent) try {
|
|
@@ -3806,7 +5138,18 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
3806
5138
|
className: (0, tailwind_merge.twMerge)("copilotKitMessage copilotKitUserMessage cpk:flex cpk:flex-col cpk:items-end cpk:group cpk:pt-10", className),
|
|
3807
5139
|
"data-message-id": message.id,
|
|
3808
5140
|
...props,
|
|
3809
|
-
children: [
|
|
5141
|
+
children: [
|
|
5142
|
+
BoundMessageRenderer,
|
|
5143
|
+
mediaParts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5144
|
+
className: "cpk:flex cpk:flex-col cpk:items-end cpk:gap-2 cpk:mt-2",
|
|
5145
|
+
children: mediaParts.map((part, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentRenderer, {
|
|
5146
|
+
type: part.type,
|
|
5147
|
+
source: part.source,
|
|
5148
|
+
filename: getFilename(part)
|
|
5149
|
+
}, index))
|
|
5150
|
+
}),
|
|
5151
|
+
BoundToolbar
|
|
5152
|
+
]
|
|
3810
5153
|
});
|
|
3811
5154
|
}
|
|
3812
5155
|
(function(_CopilotChatUserMessage) {
|
|
@@ -4104,6 +5447,7 @@ const CopilotChatSuggestionView = react.default.forwardRef(function CopilotChatS
|
|
|
4104
5447
|
const isLoading = loadingSet.has(index) || suggestion.isLoading === true;
|
|
4105
5448
|
const pill = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
|
|
4106
5449
|
children: suggestion.title,
|
|
5450
|
+
className: suggestion.className,
|
|
4107
5451
|
isLoading,
|
|
4108
5452
|
type: "button",
|
|
4109
5453
|
onClick: () => onSelectSuggestion?.(suggestion, index)
|
|
@@ -4140,9 +5484,43 @@ const CopilotChatSuggestionView = react.default.forwardRef(function CopilotChatS
|
|
|
4140
5484
|
});
|
|
4141
5485
|
CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
|
|
4142
5486
|
|
|
5487
|
+
//#endregion
|
|
5488
|
+
//#region src/v2/components/chat/scroll-element-context.ts
|
|
5489
|
+
/**
|
|
5490
|
+
* Provides the scroll container element to child components that need it for
|
|
5491
|
+
* virtualization. Set by CopilotChatView.ScrollView; consumed by
|
|
5492
|
+
* CopilotChatMessageView to feed useVirtualizer's getScrollElement.
|
|
5493
|
+
*
|
|
5494
|
+
* Carries the element itself (not a ref) so that context consumers re-render
|
|
5495
|
+
* reactively when the scroll container is first mounted.
|
|
5496
|
+
*/
|
|
5497
|
+
const ScrollElementContext = react.default.createContext(null);
|
|
5498
|
+
|
|
4143
5499
|
//#endregion
|
|
4144
5500
|
//#region src/v2/components/chat/CopilotChatMessageView.tsx
|
|
4145
5501
|
/**
|
|
5502
|
+
* Resolves a slot value into a { Component, slotProps } pair, handling the three
|
|
5503
|
+
* slot forms: a component type, a className string, or a partial-props object.
|
|
5504
|
+
*/
|
|
5505
|
+
function resolveSlotComponent(slot, DefaultComponent) {
|
|
5506
|
+
if (isReactComponentType(slot)) return {
|
|
5507
|
+
Component: slot,
|
|
5508
|
+
slotProps: void 0
|
|
5509
|
+
};
|
|
5510
|
+
if (typeof slot === "string") return {
|
|
5511
|
+
Component: DefaultComponent,
|
|
5512
|
+
slotProps: { className: slot }
|
|
5513
|
+
};
|
|
5514
|
+
if (slot && typeof slot === "object") return {
|
|
5515
|
+
Component: DefaultComponent,
|
|
5516
|
+
slotProps: slot
|
|
5517
|
+
};
|
|
5518
|
+
return {
|
|
5519
|
+
Component: DefaultComponent,
|
|
5520
|
+
slotProps: void 0
|
|
5521
|
+
};
|
|
5522
|
+
}
|
|
5523
|
+
/**
|
|
4146
5524
|
* Memoized wrapper for assistant messages to prevent re-renders when other messages change.
|
|
4147
5525
|
*/
|
|
4148
5526
|
const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
|
|
@@ -4161,7 +5539,6 @@ const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMe
|
|
|
4161
5539
|
if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
|
|
4162
5540
|
const prevTc = prevToolCalls[i];
|
|
4163
5541
|
const nextTc = nextToolCalls[i];
|
|
4164
|
-
if (!prevTc || !nextTc) return false;
|
|
4165
5542
|
if (prevTc.id !== nextTc.id) return false;
|
|
4166
5543
|
if (prevTc.function.arguments !== nextTc.function.arguments) return false;
|
|
4167
5544
|
}
|
|
@@ -4240,6 +5617,7 @@ const MemoizedCustomMessage = react.default.memo(function MemoizedCustomMessage(
|
|
|
4240
5617
|
if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
|
|
4241
5618
|
return true;
|
|
4242
5619
|
});
|
|
5620
|
+
const VIRTUALIZE_THRESHOLD = 50;
|
|
4243
5621
|
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
|
|
4244
5622
|
const renderCustomMessage = useRenderCustomMessages();
|
|
4245
5623
|
const { renderActivityMessage } = useRenderActivityMessage();
|
|
@@ -4248,12 +5626,14 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4248
5626
|
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
4249
5627
|
(0, react.useEffect)(() => {
|
|
4250
5628
|
if (!config?.agentId) return;
|
|
4251
|
-
const
|
|
5629
|
+
const registryAgent = copilotkit.getAgent(config.agentId);
|
|
5630
|
+
const agent = getThreadClone(registryAgent, config.threadId) ?? registryAgent;
|
|
4252
5631
|
if (!agent) return;
|
|
4253
5632
|
const subscription = agent.subscribe({ onStateChanged: forceUpdate });
|
|
4254
5633
|
return () => subscription.unsubscribe();
|
|
4255
5634
|
}, [
|
|
4256
5635
|
config?.agentId,
|
|
5636
|
+
config?.threadId,
|
|
4257
5637
|
copilotkit,
|
|
4258
5638
|
forceUpdate
|
|
4259
5639
|
]);
|
|
@@ -4271,9 +5651,34 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4271
5651
|
if (!resolvedRunId) return void 0;
|
|
4272
5652
|
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
|
|
4273
5653
|
};
|
|
4274
|
-
const deduplicatedMessages = [...new Map(messages.map((m) => [m.id, m])).values()];
|
|
5654
|
+
const deduplicatedMessages = (0, react.useMemo)(() => [...new Map(messages.map((m) => [m.id, m])).values()], [messages]);
|
|
4275
5655
|
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Deduplicated ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
|
|
4276
|
-
const
|
|
5656
|
+
const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
|
|
5657
|
+
const { Component: UserComponent, slotProps: userSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
|
|
5658
|
+
const { Component: ReasoningComponent, slotProps: reasoningSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(reasoningMessage, CopilotChatReasoningMessage_default), [reasoningMessage]);
|
|
5659
|
+
const scrollElementFromCtx = (0, react.useContext)(ScrollElementContext);
|
|
5660
|
+
const scrollElement = scrollElementFromCtx && scrollElementFromCtx.clientHeight > 0 ? scrollElementFromCtx : null;
|
|
5661
|
+
(0, react.useEffect)(() => {
|
|
5662
|
+
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.");
|
|
5663
|
+
}, [scrollElementFromCtx]);
|
|
5664
|
+
const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
|
|
5665
|
+
const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
|
|
5666
|
+
count: shouldVirtualize ? deduplicatedMessages.length : 0,
|
|
5667
|
+
getScrollElement: () => scrollElement,
|
|
5668
|
+
estimateSize: () => 100,
|
|
5669
|
+
overscan: 5,
|
|
5670
|
+
measureElement: (el) => el?.getBoundingClientRect().height ?? 0,
|
|
5671
|
+
initialRect: {
|
|
5672
|
+
width: 0,
|
|
5673
|
+
height: 600
|
|
5674
|
+
}
|
|
5675
|
+
});
|
|
5676
|
+
const firstMessageId = deduplicatedMessages[0]?.id;
|
|
5677
|
+
(0, react.useLayoutEffect)(() => {
|
|
5678
|
+
if (!shouldVirtualize || !deduplicatedMessages.length) return;
|
|
5679
|
+
virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
|
|
5680
|
+
}, [shouldVirtualize, firstMessageId]);
|
|
5681
|
+
const renderMessageBlock = (message) => {
|
|
4277
5682
|
const elements = [];
|
|
4278
5683
|
const stateSnapshot = getStateSnapshotForMessage(message.id);
|
|
4279
5684
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
@@ -4282,58 +5687,38 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4282
5687
|
renderCustomMessage,
|
|
4283
5688
|
stateSnapshot
|
|
4284
5689
|
}, `${message.id}-custom-before`));
|
|
4285
|
-
if (message.role === "assistant") {
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
}, message.id));
|
|
4309
|
-
} else if (message.role === "activity") {
|
|
4310
|
-
const activityMsg = message;
|
|
4311
|
-
elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
|
|
4312
|
-
message: activityMsg,
|
|
4313
|
-
renderActivityMessage
|
|
4314
|
-
}, message.id));
|
|
4315
|
-
} else if (message.role === "reasoning") {
|
|
4316
|
-
let ReasoningComponent = CopilotChatReasoningMessage_default;
|
|
4317
|
-
let reasoningSlotProps;
|
|
4318
|
-
if (isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
|
|
4319
|
-
else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
|
|
4320
|
-
else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
|
|
4321
|
-
elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
|
|
4322
|
-
message,
|
|
4323
|
-
messages,
|
|
4324
|
-
isRunning,
|
|
4325
|
-
ReasoningMessageComponent: ReasoningComponent,
|
|
4326
|
-
slotProps: reasoningSlotProps
|
|
4327
|
-
}, message.id));
|
|
4328
|
-
}
|
|
5690
|
+
if (message.role === "assistant") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
|
|
5691
|
+
message,
|
|
5692
|
+
messages,
|
|
5693
|
+
isRunning,
|
|
5694
|
+
AssistantMessageComponent: AssistantComponent,
|
|
5695
|
+
slotProps: assistantSlotProps
|
|
5696
|
+
}, message.id));
|
|
5697
|
+
else if (message.role === "user") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
|
|
5698
|
+
message,
|
|
5699
|
+
UserMessageComponent: UserComponent,
|
|
5700
|
+
slotProps: userSlotProps
|
|
5701
|
+
}, message.id));
|
|
5702
|
+
else if (message.role === "activity") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
|
|
5703
|
+
message,
|
|
5704
|
+
renderActivityMessage
|
|
5705
|
+
}, message.id));
|
|
5706
|
+
else if (message.role === "reasoning") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
|
|
5707
|
+
message,
|
|
5708
|
+
messages,
|
|
5709
|
+
isRunning,
|
|
5710
|
+
ReasoningMessageComponent: ReasoningComponent,
|
|
5711
|
+
slotProps: reasoningSlotProps
|
|
5712
|
+
}, message.id));
|
|
4329
5713
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
4330
5714
|
message,
|
|
4331
5715
|
position: "after",
|
|
4332
5716
|
renderCustomMessage,
|
|
4333
5717
|
stateSnapshot
|
|
4334
5718
|
}, `${message.id}-custom-after`));
|
|
4335
|
-
return elements;
|
|
4336
|
-
}
|
|
5719
|
+
return elements.filter(Boolean);
|
|
5720
|
+
};
|
|
5721
|
+
const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
|
|
4337
5722
|
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4338
5723
|
"data-copilotkit": true,
|
|
4339
5724
|
style: { display: "contents" },
|
|
@@ -4347,27 +5732,353 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4347
5732
|
const lastMessage = messages[messages.length - 1];
|
|
4348
5733
|
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
4349
5734
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4350
|
-
"data-copilotkit": true,
|
|
4351
|
-
"data-testid": "copilot-message-list",
|
|
4352
|
-
className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
4353
|
-
...props,
|
|
5735
|
+
"data-copilotkit": true,
|
|
5736
|
+
"data-testid": "copilot-message-list",
|
|
5737
|
+
className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
5738
|
+
...props,
|
|
5739
|
+
children: [
|
|
5740
|
+
shouldVirtualize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5741
|
+
style: {
|
|
5742
|
+
height: virtualizer.getTotalSize(),
|
|
5743
|
+
position: "relative"
|
|
5744
|
+
},
|
|
5745
|
+
children: virtualizer.getVirtualItems().map((virtualItem) => {
|
|
5746
|
+
const message = deduplicatedMessages[virtualItem.index];
|
|
5747
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5748
|
+
"data-index": virtualItem.index,
|
|
5749
|
+
ref: virtualizer.measureElement,
|
|
5750
|
+
style: {
|
|
5751
|
+
position: "absolute",
|
|
5752
|
+
top: 0,
|
|
5753
|
+
left: 0,
|
|
5754
|
+
width: "100%",
|
|
5755
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
5756
|
+
},
|
|
5757
|
+
children: renderMessageBlock(message)
|
|
5758
|
+
}, message.id);
|
|
5759
|
+
})
|
|
5760
|
+
}) : messageElements,
|
|
5761
|
+
interruptElement,
|
|
5762
|
+
showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5763
|
+
className: "cpk:mt-2",
|
|
5764
|
+
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
|
|
5765
|
+
})
|
|
5766
|
+
]
|
|
5767
|
+
});
|
|
5768
|
+
}
|
|
5769
|
+
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
5770
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5771
|
+
"data-testid": "copilot-loading-cursor",
|
|
5772
|
+
className: (0, tailwind_merge.twMerge)("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
|
|
5773
|
+
...props
|
|
5774
|
+
});
|
|
5775
|
+
};
|
|
5776
|
+
|
|
5777
|
+
//#endregion
|
|
5778
|
+
//#region src/v2/components/chat/CopilotChatAttachmentQueue.tsx
|
|
5779
|
+
const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
|
|
5780
|
+
if (attachments.length === 0) return null;
|
|
5781
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5782
|
+
className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
|
|
5783
|
+
children: attachments.map((attachment) => {
|
|
5784
|
+
const isMedia = attachment.type === "image" || attachment.type === "video";
|
|
5785
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5786
|
+
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]"),
|
|
5787
|
+
children: [
|
|
5788
|
+
attachment.status === "uploading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadingOverlay, {}),
|
|
5789
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentPreview, { attachment }),
|
|
5790
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5791
|
+
onClick: () => onRemoveAttachment(attachment.id),
|
|
5792
|
+
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"),
|
|
5793
|
+
"aria-label": "Remove attachment",
|
|
5794
|
+
children: "✕"
|
|
5795
|
+
})
|
|
5796
|
+
]
|
|
5797
|
+
}, attachment.id);
|
|
5798
|
+
})
|
|
5799
|
+
});
|
|
5800
|
+
};
|
|
5801
|
+
function UploadingOverlay() {
|
|
5802
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5803
|
+
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10",
|
|
5804
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.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" })
|
|
5805
|
+
});
|
|
5806
|
+
}
|
|
5807
|
+
function AttachmentPreview({ attachment }) {
|
|
5808
|
+
if (attachment.status === "uploading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "cpk:w-full cpk:h-full" });
|
|
5809
|
+
switch (attachment.type) {
|
|
5810
|
+
case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImagePreview, { attachment });
|
|
5811
|
+
case "audio": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AudioPreview, { attachment });
|
|
5812
|
+
case "video": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoPreview, { attachment });
|
|
5813
|
+
case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentPreview, { attachment });
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
function Lightbox({ onClose, children }) {
|
|
5817
|
+
(0, react.useEffect)(() => {
|
|
5818
|
+
const handleKey = (e) => {
|
|
5819
|
+
if (e.key === "Escape") onClose();
|
|
5820
|
+
};
|
|
5821
|
+
document.addEventListener("keydown", handleKey);
|
|
5822
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
5823
|
+
}, [onClose]);
|
|
5824
|
+
if (typeof document === "undefined") return null;
|
|
5825
|
+
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5826
|
+
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",
|
|
5827
|
+
onClick: onClose,
|
|
5828
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5829
|
+
onClick: onClose,
|
|
5830
|
+
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",
|
|
5831
|
+
"aria-label": "Close preview",
|
|
5832
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:w-5 cpk:h-5" })
|
|
5833
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5834
|
+
onClick: (e) => e.stopPropagation(),
|
|
5835
|
+
children
|
|
5836
|
+
})]
|
|
5837
|
+
}), document.body);
|
|
5838
|
+
}
|
|
5839
|
+
/**
|
|
5840
|
+
* Hook that manages lightbox open/close and uses the View Transition API to
|
|
5841
|
+
* morph the thumbnail into fullscreen content.
|
|
5842
|
+
*
|
|
5843
|
+
* The trick: `view-transition-name` must live on exactly ONE element at a time.
|
|
5844
|
+
* - Old state (thumbnail visible): name is on the thumbnail.
|
|
5845
|
+
* - New state (lightbox visible): name moves to the lightbox content.
|
|
5846
|
+
* `flushSync` ensures React commits the DOM change synchronously inside the
|
|
5847
|
+
* `startViewTransition` callback so the API can snapshot old → new correctly.
|
|
5848
|
+
*/
|
|
5849
|
+
function useLightbox() {
|
|
5850
|
+
const thumbnailRef = (0, react.useRef)(null);
|
|
5851
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
5852
|
+
const vtName = (0, react.useId)();
|
|
5853
|
+
return {
|
|
5854
|
+
thumbnailRef,
|
|
5855
|
+
vtName,
|
|
5856
|
+
open,
|
|
5857
|
+
openLightbox: (0, react.useCallback)(() => {
|
|
5858
|
+
const thumb = thumbnailRef.current;
|
|
5859
|
+
const doc = document;
|
|
5860
|
+
if (doc.startViewTransition && thumb) {
|
|
5861
|
+
thumb.style.viewTransitionName = vtName;
|
|
5862
|
+
doc.startViewTransition(() => {
|
|
5863
|
+
thumb.style.viewTransitionName = "";
|
|
5864
|
+
(0, react_dom.flushSync)(() => setOpen(true));
|
|
5865
|
+
});
|
|
5866
|
+
} else setOpen(true);
|
|
5867
|
+
}, []),
|
|
5868
|
+
closeLightbox: (0, react.useCallback)(() => {
|
|
5869
|
+
const thumb = thumbnailRef.current;
|
|
5870
|
+
const doc = document;
|
|
5871
|
+
if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
|
|
5872
|
+
(0, react_dom.flushSync)(() => setOpen(false));
|
|
5873
|
+
thumb.style.viewTransitionName = vtName;
|
|
5874
|
+
}).finished.then(() => {
|
|
5875
|
+
thumb.style.viewTransitionName = "";
|
|
5876
|
+
}).catch(() => {
|
|
5877
|
+
thumb.style.viewTransitionName = "";
|
|
5878
|
+
});
|
|
5879
|
+
else setOpen(false);
|
|
5880
|
+
}, [])
|
|
5881
|
+
};
|
|
5882
|
+
}
|
|
5883
|
+
function ImagePreview({ attachment }) {
|
|
5884
|
+
const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
|
|
5885
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5886
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5887
|
+
ref: thumbnailRef,
|
|
5888
|
+
src,
|
|
5889
|
+
alt: attachment.filename || "Image attachment",
|
|
5890
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover cpk:cursor-pointer",
|
|
5891
|
+
onClick: openLightbox
|
|
5892
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
|
|
5893
|
+
onClose: closeLightbox,
|
|
5894
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5895
|
+
style: { viewTransitionName: vtName },
|
|
5896
|
+
src,
|
|
5897
|
+
alt: attachment.filename || "Image attachment",
|
|
5898
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
|
|
5899
|
+
})
|
|
5900
|
+
})] });
|
|
5901
|
+
}
|
|
5902
|
+
function AudioPreview({ attachment }) {
|
|
5903
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5904
|
+
className: "cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full",
|
|
5905
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
|
|
5906
|
+
src: (0, _copilotkit_shared.getSourceUrl)(attachment.source),
|
|
5907
|
+
controls: true,
|
|
5908
|
+
preload: "metadata",
|
|
5909
|
+
className: "cpk:w-full cpk:h-8"
|
|
5910
|
+
}), attachment.filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5911
|
+
className: "cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap",
|
|
5912
|
+
children: attachment.filename
|
|
5913
|
+
})]
|
|
5914
|
+
});
|
|
5915
|
+
}
|
|
5916
|
+
function VideoPreview({ attachment }) {
|
|
5917
|
+
const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
|
|
5918
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5919
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5920
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5921
|
+
ref: thumbnailRef,
|
|
5922
|
+
className: "cpk:w-full cpk:h-full",
|
|
5923
|
+
children: attachment.thumbnail ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5924
|
+
src: attachment.thumbnail,
|
|
5925
|
+
alt: attachment.filename || "Video thumbnail",
|
|
5926
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover"
|
|
5927
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
|
|
5928
|
+
src,
|
|
5929
|
+
preload: "metadata",
|
|
5930
|
+
muted: true,
|
|
5931
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover"
|
|
5932
|
+
})
|
|
5933
|
+
}),
|
|
5934
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5935
|
+
onClick: openLightbox,
|
|
5936
|
+
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",
|
|
5937
|
+
"aria-label": "Play video",
|
|
5938
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5939
|
+
className: "cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center",
|
|
5940
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Play, { className: "cpk:w-4 cpk:h-4 cpk:text-white cpk:ml-0.5" })
|
|
5941
|
+
})
|
|
5942
|
+
}),
|
|
5943
|
+
open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
|
|
5944
|
+
onClose: closeLightbox,
|
|
5945
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
|
|
5946
|
+
style: { viewTransitionName: vtName },
|
|
5947
|
+
src,
|
|
5948
|
+
controls: true,
|
|
5949
|
+
autoPlay: true,
|
|
5950
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
|
|
5951
|
+
})
|
|
5952
|
+
})
|
|
5953
|
+
] });
|
|
5954
|
+
}
|
|
5955
|
+
function isPdf(mimeType) {
|
|
5956
|
+
return !!mimeType && mimeType.includes("pdf");
|
|
5957
|
+
}
|
|
5958
|
+
function isText(mimeType) {
|
|
5959
|
+
return !!mimeType && mimeType.startsWith("text/");
|
|
5960
|
+
}
|
|
5961
|
+
function canPreviewInBrowser(mimeType) {
|
|
5962
|
+
return isPdf(mimeType) || isText(mimeType);
|
|
5963
|
+
}
|
|
5964
|
+
/**
|
|
5965
|
+
* Convert a base64-encoded data source to a blob: URL that browsers will
|
|
5966
|
+
* render inside an iframe (data: URLs are blocked for PDFs in most browsers).
|
|
5967
|
+
*/
|
|
5968
|
+
function useBlobUrl(attachment) {
|
|
5969
|
+
const [url, setUrl] = (0, react.useState)(null);
|
|
5970
|
+
(0, react.useEffect)(() => {
|
|
5971
|
+
if (attachment.source.type !== "data") return;
|
|
5972
|
+
try {
|
|
5973
|
+
const binary = atob(attachment.source.value);
|
|
5974
|
+
const bytes = new Uint8Array(binary.length);
|
|
5975
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
5976
|
+
const blob = new Blob([bytes], { type: attachment.source.mimeType || "application/octet-stream" });
|
|
5977
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
5978
|
+
setUrl(blobUrl);
|
|
5979
|
+
return () => URL.revokeObjectURL(blobUrl);
|
|
5980
|
+
} catch (error) {
|
|
5981
|
+
console.error("[CopilotKit] Failed to decode attachment data:", error);
|
|
5982
|
+
setUrl(null);
|
|
5983
|
+
}
|
|
5984
|
+
}, [
|
|
5985
|
+
attachment.source.type,
|
|
5986
|
+
attachment.source.value,
|
|
5987
|
+
attachment.source.mimeType
|
|
5988
|
+
]);
|
|
5989
|
+
if (attachment.source.type === "url") return attachment.source.value;
|
|
5990
|
+
return url;
|
|
5991
|
+
}
|
|
5992
|
+
function DocumentLightboxContent({ attachment, vtName }) {
|
|
5993
|
+
const mimeType = attachment.source.mimeType;
|
|
5994
|
+
const blobUrl = useBlobUrl(attachment);
|
|
5995
|
+
if (isPdf(mimeType)) {
|
|
5996
|
+
if (!blobUrl) return null;
|
|
5997
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
|
|
5998
|
+
style: { viewTransitionName: vtName },
|
|
5999
|
+
src: blobUrl,
|
|
6000
|
+
title: attachment.filename || "PDF preview",
|
|
6001
|
+
className: "cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
|
|
6002
|
+
});
|
|
6003
|
+
}
|
|
6004
|
+
if (isText(mimeType)) {
|
|
6005
|
+
const textContent = attachment.source.type === "data" ? (() => {
|
|
6006
|
+
try {
|
|
6007
|
+
return atob(attachment.source.value);
|
|
6008
|
+
} catch {
|
|
6009
|
+
return attachment.source.value;
|
|
6010
|
+
}
|
|
6011
|
+
})() : null;
|
|
6012
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6013
|
+
style: { viewTransitionName: vtName },
|
|
6014
|
+
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",
|
|
6015
|
+
children: [attachment.filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6016
|
+
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",
|
|
6017
|
+
children: attachment.filename
|
|
6018
|
+
}), textContent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
6019
|
+
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",
|
|
6020
|
+
children: textContent
|
|
6021
|
+
}) : blobUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
|
|
6022
|
+
src: blobUrl,
|
|
6023
|
+
title: attachment.filename || "Text preview",
|
|
6024
|
+
className: "cpk:w-full cpk:h-[80vh] cpk:border-none"
|
|
6025
|
+
}) : null]
|
|
6026
|
+
});
|
|
6027
|
+
}
|
|
6028
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6029
|
+
style: { viewTransitionName: vtName },
|
|
6030
|
+
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",
|
|
4354
6031
|
children: [
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
6032
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6033
|
+
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",
|
|
6034
|
+
children: (0, _copilotkit_shared.getDocumentIcon)(mimeType ?? "")
|
|
6035
|
+
}),
|
|
6036
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6037
|
+
className: "cpk:text-center",
|
|
6038
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6039
|
+
className: "cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200",
|
|
6040
|
+
children: attachment.filename || "Document"
|
|
6041
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6042
|
+
className: "cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1",
|
|
6043
|
+
children: [mimeType || "Unknown type", attachment.size != null && ` · ${(0, _copilotkit_shared.formatFileSize)(attachment.size)}`]
|
|
6044
|
+
})]
|
|
6045
|
+
}),
|
|
6046
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6047
|
+
className: "cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500",
|
|
6048
|
+
children: "No preview available for this file type"
|
|
4360
6049
|
})
|
|
4361
6050
|
]
|
|
4362
6051
|
});
|
|
4363
6052
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
6053
|
+
function DocumentPreview({ attachment }) {
|
|
6054
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
6055
|
+
const mimeType = attachment.source.mimeType;
|
|
6056
|
+
const previewable = canPreviewInBrowser(mimeType);
|
|
6057
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6058
|
+
ref: thumbnailRef,
|
|
6059
|
+
className: cn("cpk:flex cpk:items-center cpk:gap-2", previewable && "cpk:cursor-pointer"),
|
|
6060
|
+
onClick: previewable ? openLightbox : void 0,
|
|
6061
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6062
|
+
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",
|
|
6063
|
+
children: (0, _copilotkit_shared.getDocumentIcon)(mimeType ?? "")
|
|
6064
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6065
|
+
className: "cpk:flex cpk:flex-col cpk:min-w-0",
|
|
6066
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6067
|
+
className: "cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight",
|
|
6068
|
+
children: attachment.filename || "Document"
|
|
6069
|
+
}), attachment.size != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6070
|
+
className: "cpk:text-[11px] cpk:text-muted-foreground",
|
|
6071
|
+
children: (0, _copilotkit_shared.formatFileSize)(attachment.size)
|
|
6072
|
+
})]
|
|
6073
|
+
})]
|
|
6074
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
|
|
6075
|
+
onClose: closeLightbox,
|
|
6076
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentLightboxContent, {
|
|
6077
|
+
attachment,
|
|
6078
|
+
vtName
|
|
6079
|
+
})
|
|
6080
|
+
})] });
|
|
6081
|
+
}
|
|
4371
6082
|
|
|
4372
6083
|
//#endregion
|
|
4373
6084
|
//#region src/v2/hooks/use-keyboard-height.tsx
|
|
@@ -4413,7 +6124,19 @@ function useKeyboardHeight() {
|
|
|
4413
6124
|
//#endregion
|
|
4414
6125
|
//#region src/v2/components/chat/CopilotChatView.tsx
|
|
4415
6126
|
const FEATHER_HEIGHT = 96;
|
|
4416
|
-
function
|
|
6127
|
+
function DropOverlay() {
|
|
6128
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6129
|
+
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"),
|
|
6130
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6131
|
+
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-2 cpk:text-primary/70",
|
|
6132
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Upload, { className: "cpk:w-8 cpk:h-8" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6133
|
+
className: "cpk:text-sm cpk:font-medium",
|
|
6134
|
+
children: "Drop files here"
|
|
6135
|
+
})]
|
|
6136
|
+
})
|
|
6137
|
+
});
|
|
6138
|
+
}
|
|
6139
|
+
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 }) {
|
|
4417
6140
|
const inputContainerRef = (0, react.useRef)(null);
|
|
4418
6141
|
const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
|
|
4419
6142
|
const [isResizing, setIsResizing] = (0, react.useState)(false);
|
|
@@ -4460,6 +6183,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4460
6183
|
onCancelTranscribe,
|
|
4461
6184
|
onFinishTranscribe,
|
|
4462
6185
|
onFinishTranscribeWithAudio,
|
|
6186
|
+
onAddFile,
|
|
4463
6187
|
positioning: "static",
|
|
4464
6188
|
keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
|
|
4465
6189
|
containerRef: inputContainerRef,
|
|
@@ -4500,21 +6224,34 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4500
6224
|
onCancelTranscribe,
|
|
4501
6225
|
onFinishTranscribe,
|
|
4502
6226
|
onFinishTranscribeWithAudio,
|
|
6227
|
+
onAddFile,
|
|
4503
6228
|
positioning: "static",
|
|
4504
6229
|
showDisclaimer: true,
|
|
4505
6230
|
...disclaimer !== void 0 ? { disclaimer } : {}
|
|
4506
6231
|
});
|
|
4507
|
-
const
|
|
4508
|
-
|
|
6232
|
+
const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
|
|
6233
|
+
const inputWithAttachments = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6234
|
+
className: "cpk:w-full",
|
|
6235
|
+
children: [attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
|
|
6236
|
+
attachments,
|
|
6237
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6238
|
+
className: "cpk:mb-2"
|
|
6239
|
+
}), BoundInputForWelcome]
|
|
6240
|
+
});
|
|
6241
|
+
const BoundWelcomeScreen = renderSlot(welcomeScreenSlot, CopilotChatView.WelcomeScreen, {
|
|
6242
|
+
input: inputWithAttachments,
|
|
4509
6243
|
suggestionView: BoundSuggestionView ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {})
|
|
4510
6244
|
});
|
|
4511
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6245
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4512
6246
|
"data-copilotkit": true,
|
|
4513
6247
|
"data-testid": "copilot-chat",
|
|
4514
6248
|
"data-copilot-running": isRunning ? "true" : "false",
|
|
4515
|
-
|
|
6249
|
+
onDragOver,
|
|
6250
|
+
onDragLeave,
|
|
6251
|
+
onDrop,
|
|
6252
|
+
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
4516
6253
|
...props,
|
|
4517
|
-
children: BoundWelcomeScreen
|
|
6254
|
+
children: [dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}), BoundWelcomeScreen]
|
|
4518
6255
|
});
|
|
4519
6256
|
}
|
|
4520
6257
|
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4531,39 +6268,66 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4531
6268
|
"data-copilotkit": true,
|
|
4532
6269
|
"data-testid": "copilot-chat",
|
|
4533
6270
|
"data-copilot-running": isRunning ? "true" : "false",
|
|
4534
|
-
|
|
6271
|
+
onDragOver,
|
|
6272
|
+
onDragLeave,
|
|
6273
|
+
onDrop,
|
|
6274
|
+
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
4535
6275
|
...props,
|
|
4536
|
-
children: [
|
|
6276
|
+
children: [
|
|
6277
|
+
dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
|
|
6278
|
+
BoundScrollView,
|
|
6279
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6280
|
+
className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full",
|
|
6281
|
+
children: attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
|
|
6282
|
+
attachments,
|
|
6283
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6284
|
+
className: "cpk:px-4"
|
|
6285
|
+
})
|
|
6286
|
+
}),
|
|
6287
|
+
BoundInput
|
|
6288
|
+
]
|
|
4537
6289
|
});
|
|
4538
6290
|
}
|
|
4539
6291
|
(function(_CopilotChatView) {
|
|
4540
6292
|
const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
|
|
4541
|
-
const { isAtBottom, scrollToBottom } = (0, use_stick_to_bottom.useStickToBottomContext)();
|
|
6293
|
+
const { isAtBottom, scrollToBottom, scrollRef } = (0, use_stick_to_bottom.useStickToBottomContext)();
|
|
6294
|
+
const [scrollEl, setScrollEl] = (0, react.useState)(null);
|
|
6295
|
+
(0, react.useLayoutEffect)(() => {
|
|
6296
|
+
setScrollEl(scrollRef.current ?? null);
|
|
6297
|
+
}, []);
|
|
4542
6298
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
4543
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
children
|
|
6299
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
|
|
6300
|
+
value: scrollEl,
|
|
6301
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6302
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom.Content, {
|
|
6303
|
+
className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
6304
|
+
style: {
|
|
6305
|
+
flex: "1 1 0%",
|
|
6306
|
+
minHeight: 0
|
|
6307
|
+
},
|
|
6308
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6309
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6310
|
+
children
|
|
6311
|
+
})
|
|
6312
|
+
}),
|
|
6313
|
+
BoundFeather,
|
|
6314
|
+
!isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6315
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6316
|
+
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
6317
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
4553
6318
|
})
|
|
4554
|
-
})
|
|
4555
|
-
|
|
4556
|
-
!isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4557
|
-
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
4558
|
-
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
4559
|
-
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
4560
|
-
})
|
|
4561
|
-
] });
|
|
6319
|
+
] })
|
|
6320
|
+
});
|
|
4562
6321
|
};
|
|
4563
6322
|
_CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
|
|
4564
6323
|
const [hasMounted, setHasMounted] = (0, react.useState)(false);
|
|
4565
6324
|
const { scrollRef, contentRef, scrollToBottom } = (0, use_stick_to_bottom.useStickToBottom)();
|
|
4566
6325
|
const [showScrollButton, setShowScrollButton] = (0, react.useState)(false);
|
|
6326
|
+
const [nonAutoScrollEl, setNonAutoScrollEl] = (0, react.useState)(null);
|
|
6327
|
+
const nonAutoScrollRefCallback = (0, react.useCallback)((el) => {
|
|
6328
|
+
scrollRef.current = el;
|
|
6329
|
+
setNonAutoScrollEl(el);
|
|
6330
|
+
}, []);
|
|
4567
6331
|
(0, react.useEffect)(() => {
|
|
4568
6332
|
setHasMounted(true);
|
|
4569
6333
|
}, []);
|
|
@@ -4592,23 +6356,26 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4592
6356
|
});
|
|
4593
6357
|
if (!autoScroll) {
|
|
4594
6358
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
4595
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
6359
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
|
|
6360
|
+
value: nonAutoScrollEl,
|
|
6361
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6362
|
+
ref: nonAutoScrollRefCallback,
|
|
6363
|
+
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),
|
|
6364
|
+
...props,
|
|
6365
|
+
children: [
|
|
6366
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6367
|
+
ref: contentRef,
|
|
6368
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6369
|
+
children
|
|
6370
|
+
}),
|
|
6371
|
+
BoundFeather,
|
|
6372
|
+
showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6373
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6374
|
+
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
6375
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
6376
|
+
})
|
|
6377
|
+
]
|
|
6378
|
+
})
|
|
4612
6379
|
});
|
|
4613
6380
|
}
|
|
4614
6381
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom, {
|
|
@@ -4800,11 +6567,15 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
|
|
|
4800
6567
|
|
|
4801
6568
|
//#endregion
|
|
4802
6569
|
//#region src/v2/components/chat/CopilotChat.tsx
|
|
4803
|
-
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, onError, ...props }) {
|
|
6570
|
+
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
|
|
4804
6571
|
const existingConfig = useCopilotChatConfiguration();
|
|
4805
6572
|
const resolvedAgentId = agentId ?? existingConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
4806
6573
|
const resolvedThreadId = (0, react.useMemo)(() => threadId ?? existingConfig?.threadId ?? (0, _copilotkit_shared.randomUUID)(), [threadId, existingConfig?.threadId]);
|
|
4807
|
-
const { agent } = useAgent({
|
|
6574
|
+
const { agent } = useAgent({
|
|
6575
|
+
agentId: resolvedAgentId,
|
|
6576
|
+
threadId: resolvedThreadId,
|
|
6577
|
+
throttleMs
|
|
6578
|
+
});
|
|
4808
6579
|
const { copilotkit } = useCopilotKit();
|
|
4809
6580
|
const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
|
|
4810
6581
|
const { checkFeature } = useLicenseContext();
|
|
@@ -4833,6 +6604,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4833
6604
|
const [inputValue, setInputValue] = (0, react.useState)("");
|
|
4834
6605
|
const [transcriptionError, setTranscriptionError] = (0, react.useState)(null);
|
|
4835
6606
|
const [isTranscribing, setIsTranscribing] = (0, react.useState)(false);
|
|
6607
|
+
const { attachments: selectedAttachments, enabled: attachmentsEnabled, dragOver, fileInputRef, containerRef: chatContainerRef, handleFileUpload, handleDragOver, handleDragLeave, handleDrop, removeAttachment, consumeAttachments } = useAttachments({ config: attachmentsConfig });
|
|
4836
6608
|
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
4837
6609
|
const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
|
|
4838
6610
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
@@ -4848,7 +6620,6 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4848
6620
|
console.error("CopilotChat: connectAgent failed", error);
|
|
4849
6621
|
}
|
|
4850
6622
|
};
|
|
4851
|
-
agent.threadId = resolvedThreadId;
|
|
4852
6623
|
connect(agent);
|
|
4853
6624
|
return () => {
|
|
4854
6625
|
detached = true;
|
|
@@ -4861,7 +6632,31 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4861
6632
|
resolvedAgentId
|
|
4862
6633
|
]);
|
|
4863
6634
|
const onSubmitInput = (0, react.useCallback)(async (value) => {
|
|
4864
|
-
|
|
6635
|
+
if (selectedAttachments.some((a) => a.status === "uploading")) {
|
|
6636
|
+
console.error("[CopilotKit] Cannot send while attachments are uploading");
|
|
6637
|
+
return;
|
|
6638
|
+
}
|
|
6639
|
+
const readyAttachments = consumeAttachments();
|
|
6640
|
+
if (readyAttachments.length > 0) {
|
|
6641
|
+
const contentParts = [];
|
|
6642
|
+
if (value.trim()) contentParts.push({
|
|
6643
|
+
type: "text",
|
|
6644
|
+
text: value
|
|
6645
|
+
});
|
|
6646
|
+
for (const att of readyAttachments) contentParts.push({
|
|
6647
|
+
type: att.type,
|
|
6648
|
+
source: att.source,
|
|
6649
|
+
metadata: {
|
|
6650
|
+
...att.filename ? { filename: att.filename } : {},
|
|
6651
|
+
...att.metadata
|
|
6652
|
+
}
|
|
6653
|
+
});
|
|
6654
|
+
agent.addMessage({
|
|
6655
|
+
id: (0, _copilotkit_shared.randomUUID)(),
|
|
6656
|
+
role: "user",
|
|
6657
|
+
content: contentParts
|
|
6658
|
+
});
|
|
6659
|
+
} else agent.addMessage({
|
|
4865
6660
|
id: (0, _copilotkit_shared.randomUUID)(),
|
|
4866
6661
|
role: "user",
|
|
4867
6662
|
content: value
|
|
@@ -4872,7 +6667,11 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4872
6667
|
} catch (error) {
|
|
4873
6668
|
console.error("CopilotChat: runAgent failed", error);
|
|
4874
6669
|
}
|
|
4875
|
-
}, [
|
|
6670
|
+
}, [
|
|
6671
|
+
agent,
|
|
6672
|
+
selectedAttachments,
|
|
6673
|
+
consumeAttachments
|
|
6674
|
+
]);
|
|
4876
6675
|
const handleSelectSuggestion = (0, react.useCallback)(async (suggestion) => {
|
|
4877
6676
|
agent.addMessage({
|
|
4878
6677
|
id: (0, _copilotkit_shared.randomUUID)(),
|
|
@@ -4959,21 +6758,33 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4959
6758
|
return () => clearTimeout(timer);
|
|
4960
6759
|
}
|
|
4961
6760
|
}, [transcriptionError]);
|
|
4962
|
-
const
|
|
6761
|
+
const stableMessageView = useShallowStableRef(typeof providedMessageView === "string" ? { className: providedMessageView } : providedMessageView);
|
|
6762
|
+
const stableSuggestionView = useShallowStableRef(providedSuggestionView);
|
|
6763
|
+
const handleAddFile = (0, react.useCallback)(() => {
|
|
6764
|
+
setTimeout(() => {
|
|
6765
|
+
fileInputRef.current?.click();
|
|
6766
|
+
}, 100);
|
|
6767
|
+
}, []);
|
|
6768
|
+
const mergedProps = {
|
|
4963
6769
|
isRunning: agent.isRunning,
|
|
4964
6770
|
suggestions: autoSuggestions,
|
|
4965
6771
|
onSelectSuggestion: handleSelectSuggestion,
|
|
4966
|
-
suggestionView:
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
});
|
|
6772
|
+
suggestionView: stableSuggestionView,
|
|
6773
|
+
...restProps
|
|
6774
|
+
};
|
|
6775
|
+
if (stableMessageView !== void 0) mergedProps.messageView = stableMessageView;
|
|
4971
6776
|
const hasMessages = agent.messages.length > 0;
|
|
4972
6777
|
const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
|
|
4973
6778
|
const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
|
|
4974
6779
|
const effectiveMode = isTranscribing ? "processing" : transcribeMode;
|
|
4975
|
-
const
|
|
4976
|
-
|
|
6780
|
+
const messages = (0, react.useMemo)(() => [...agent.messages], [agent.messages.map((m) => {
|
|
6781
|
+
const contentKey = typeof m.content === "string" ? m.content.length : Array.isArray(m.content) ? m.content.length : 0;
|
|
6782
|
+
const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
|
|
6783
|
+
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
|
|
6784
|
+
}).join(",")]);
|
|
6785
|
+
const RenderedChatView = renderSlot(chatView, CopilotChatView, {
|
|
6786
|
+
...mergedProps,
|
|
6787
|
+
messages,
|
|
4977
6788
|
onSubmitMessage: onSubmitInput,
|
|
4978
6789
|
onStop: effectiveStopHandler,
|
|
4979
6790
|
inputMode: effectiveMode,
|
|
@@ -4982,32 +6793,51 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4982
6793
|
onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
|
|
4983
6794
|
onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
|
|
4984
6795
|
onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
|
|
4985
|
-
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
|
|
4986
|
-
|
|
4987
|
-
|
|
6796
|
+
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0,
|
|
6797
|
+
attachments: selectedAttachments,
|
|
6798
|
+
onRemoveAttachment: removeAttachment,
|
|
6799
|
+
onAddFile: attachmentsEnabled ? handleAddFile : void 0,
|
|
6800
|
+
dragOver,
|
|
6801
|
+
onDragOver: handleDragOver,
|
|
6802
|
+
onDragLeave: handleDragLeave,
|
|
6803
|
+
onDrop: handleDrop
|
|
6804
|
+
});
|
|
6805
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
|
|
4988
6806
|
agentId: resolvedAgentId,
|
|
4989
6807
|
threadId: resolvedThreadId,
|
|
4990
6808
|
labels,
|
|
4991
6809
|
isModalDefaultOpen,
|
|
4992
|
-
children:
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
6810
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6811
|
+
ref: chatContainerRef,
|
|
6812
|
+
style: { display: "contents" },
|
|
6813
|
+
children: [
|
|
6814
|
+
attachmentsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6815
|
+
type: "file",
|
|
6816
|
+
multiple: true,
|
|
6817
|
+
ref: fileInputRef,
|
|
6818
|
+
onChange: handleFileUpload,
|
|
6819
|
+
accept: attachmentsConfig?.accept ?? "*/*",
|
|
6820
|
+
style: { display: "none" }
|
|
6821
|
+
}),
|
|
6822
|
+
!isChatLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Chat" }),
|
|
6823
|
+
transcriptionError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6824
|
+
style: {
|
|
6825
|
+
position: "absolute",
|
|
6826
|
+
bottom: "100px",
|
|
6827
|
+
left: "50%",
|
|
6828
|
+
transform: "translateX(-50%)",
|
|
6829
|
+
backgroundColor: "#ef4444",
|
|
6830
|
+
color: "white",
|
|
6831
|
+
padding: "8px 16px",
|
|
6832
|
+
borderRadius: "8px",
|
|
6833
|
+
fontSize: "14px",
|
|
6834
|
+
zIndex: 50
|
|
6835
|
+
},
|
|
6836
|
+
children: transcriptionError
|
|
6837
|
+
}),
|
|
6838
|
+
RenderedChatView
|
|
6839
|
+
]
|
|
6840
|
+
})
|
|
5011
6841
|
});
|
|
5012
6842
|
}
|
|
5013
6843
|
(function(_CopilotChat) {
|
|
@@ -5872,6 +7702,227 @@ function useToast() {
|
|
|
5872
7702
|
if (!context) throw new Error("useToast must be used within a ToastProvider");
|
|
5873
7703
|
return context;
|
|
5874
7704
|
}
|
|
7705
|
+
function formatBannerMessage(message) {
|
|
7706
|
+
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
|
|
7707
|
+
if (jsonMatch) return jsonMatch[1];
|
|
7708
|
+
let cleaned = message.split(" - ")[0];
|
|
7709
|
+
cleaned = cleaned.split(": Error code")[0];
|
|
7710
|
+
cleaned = cleaned.replace(/:\s*\d{3}$/, "");
|
|
7711
|
+
cleaned = cleaned.replace(/See more:.*$/g, "");
|
|
7712
|
+
cleaned = cleaned.trim();
|
|
7713
|
+
return cleaned || "An error occurred.";
|
|
7714
|
+
}
|
|
7715
|
+
function extractUrl(message) {
|
|
7716
|
+
const markdownMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(message);
|
|
7717
|
+
if (markdownMatch) return {
|
|
7718
|
+
url: markdownMatch[2],
|
|
7719
|
+
text: "See More"
|
|
7720
|
+
};
|
|
7721
|
+
const plainMatch = /(https?:\/\/[^\s)]+)/.exec(message);
|
|
7722
|
+
if (plainMatch) return {
|
|
7723
|
+
url: plainMatch[0].replace(/[.,;:'"]*$/, ""),
|
|
7724
|
+
text: "See More"
|
|
7725
|
+
};
|
|
7726
|
+
return null;
|
|
7727
|
+
}
|
|
7728
|
+
function BannerErrorDisplay({ bannerError, onDismiss }) {
|
|
7729
|
+
const [detailsExpanded, setDetailsExpanded] = (0, react.useState)(false);
|
|
7730
|
+
const colors = getErrorColors(getErrorSeverity(bannerError));
|
|
7731
|
+
const details = bannerError.details;
|
|
7732
|
+
const link = extractUrl(bannerError.message);
|
|
7733
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7734
|
+
style: {
|
|
7735
|
+
position: "fixed",
|
|
7736
|
+
bottom: "20px",
|
|
7737
|
+
left: "50%",
|
|
7738
|
+
transform: "translateX(-50%)",
|
|
7739
|
+
zIndex: 9999,
|
|
7740
|
+
backgroundColor: colors.background,
|
|
7741
|
+
border: `1px solid ${colors.border}`,
|
|
7742
|
+
borderLeft: `4px solid ${colors.border}`,
|
|
7743
|
+
borderRadius: "8px",
|
|
7744
|
+
padding: "12px 16px",
|
|
7745
|
+
fontSize: "13px",
|
|
7746
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
7747
|
+
backdropFilter: "blur(8px)",
|
|
7748
|
+
maxWidth: "min(90vw, 700px)",
|
|
7749
|
+
width: "100%",
|
|
7750
|
+
boxSizing: "border-box",
|
|
7751
|
+
overflow: "hidden"
|
|
7752
|
+
},
|
|
7753
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7754
|
+
style: {
|
|
7755
|
+
display: "flex",
|
|
7756
|
+
justifyContent: "space-between",
|
|
7757
|
+
alignItems: "center",
|
|
7758
|
+
gap: "10px"
|
|
7759
|
+
},
|
|
7760
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7761
|
+
style: {
|
|
7762
|
+
display: "flex",
|
|
7763
|
+
alignItems: "center",
|
|
7764
|
+
gap: "8px",
|
|
7765
|
+
flex: 1,
|
|
7766
|
+
minWidth: 0
|
|
7767
|
+
},
|
|
7768
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
7769
|
+
width: "12px",
|
|
7770
|
+
height: "12px",
|
|
7771
|
+
borderRadius: "50%",
|
|
7772
|
+
backgroundColor: colors.border,
|
|
7773
|
+
flexShrink: 0
|
|
7774
|
+
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7775
|
+
style: {
|
|
7776
|
+
display: "flex",
|
|
7777
|
+
alignItems: "center",
|
|
7778
|
+
gap: "10px",
|
|
7779
|
+
flex: 1,
|
|
7780
|
+
minWidth: 0
|
|
7781
|
+
},
|
|
7782
|
+
children: [
|
|
7783
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7784
|
+
style: {
|
|
7785
|
+
color: colors.text,
|
|
7786
|
+
lineHeight: "1.4",
|
|
7787
|
+
fontWeight: "400",
|
|
7788
|
+
fontSize: "13px",
|
|
7789
|
+
flex: 1,
|
|
7790
|
+
wordBreak: "break-all",
|
|
7791
|
+
overflowWrap: "break-word",
|
|
7792
|
+
maxWidth: "550px",
|
|
7793
|
+
overflow: "hidden",
|
|
7794
|
+
display: "-webkit-box",
|
|
7795
|
+
WebkitLineClamp: 10,
|
|
7796
|
+
WebkitBoxOrient: "vertical"
|
|
7797
|
+
},
|
|
7798
|
+
children: formatBannerMessage(bannerError.message)
|
|
7799
|
+
}),
|
|
7800
|
+
link && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7801
|
+
onClick: () => window.open(link.url, "_blank", "noopener,noreferrer"),
|
|
7802
|
+
style: {
|
|
7803
|
+
background: colors.border,
|
|
7804
|
+
color: "white",
|
|
7805
|
+
border: "none",
|
|
7806
|
+
borderRadius: "5px",
|
|
7807
|
+
padding: "4px 10px",
|
|
7808
|
+
fontSize: "11px",
|
|
7809
|
+
fontWeight: "500",
|
|
7810
|
+
cursor: "pointer",
|
|
7811
|
+
transition: "all 0.2s ease",
|
|
7812
|
+
flexShrink: 0
|
|
7813
|
+
},
|
|
7814
|
+
onMouseEnter: (e) => {
|
|
7815
|
+
e.currentTarget.style.opacity = "0.9";
|
|
7816
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
7817
|
+
},
|
|
7818
|
+
onMouseLeave: (e) => {
|
|
7819
|
+
e.currentTarget.style.opacity = "1";
|
|
7820
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
7821
|
+
},
|
|
7822
|
+
children: link.text
|
|
7823
|
+
}),
|
|
7824
|
+
details && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7825
|
+
onClick: () => setDetailsExpanded(!detailsExpanded),
|
|
7826
|
+
style: {
|
|
7827
|
+
background: "transparent",
|
|
7828
|
+
border: `1px solid ${colors.border}`,
|
|
7829
|
+
borderRadius: "5px",
|
|
7830
|
+
padding: "4px 10px",
|
|
7831
|
+
fontSize: "11px",
|
|
7832
|
+
fontWeight: "500",
|
|
7833
|
+
cursor: "pointer",
|
|
7834
|
+
color: colors.text,
|
|
7835
|
+
flexShrink: 0,
|
|
7836
|
+
transition: "all 0.2s ease"
|
|
7837
|
+
},
|
|
7838
|
+
onMouseEnter: (e) => {
|
|
7839
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
7840
|
+
},
|
|
7841
|
+
onMouseLeave: (e) => {
|
|
7842
|
+
e.currentTarget.style.background = "transparent";
|
|
7843
|
+
},
|
|
7844
|
+
children: detailsExpanded ? "Hide Details" : "Show Details"
|
|
7845
|
+
})
|
|
7846
|
+
]
|
|
7847
|
+
})]
|
|
7848
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7849
|
+
onClick: onDismiss,
|
|
7850
|
+
style: {
|
|
7851
|
+
background: "transparent",
|
|
7852
|
+
border: "none",
|
|
7853
|
+
color: colors.text,
|
|
7854
|
+
cursor: "pointer",
|
|
7855
|
+
padding: "2px",
|
|
7856
|
+
borderRadius: "3px",
|
|
7857
|
+
fontSize: "14px",
|
|
7858
|
+
lineHeight: "1",
|
|
7859
|
+
opacity: .6,
|
|
7860
|
+
transition: "all 0.2s ease",
|
|
7861
|
+
flexShrink: 0
|
|
7862
|
+
},
|
|
7863
|
+
title: "Dismiss",
|
|
7864
|
+
onMouseEnter: (e) => {
|
|
7865
|
+
e.currentTarget.style.opacity = "1";
|
|
7866
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
7867
|
+
},
|
|
7868
|
+
onMouseLeave: (e) => {
|
|
7869
|
+
e.currentTarget.style.opacity = "0.6";
|
|
7870
|
+
e.currentTarget.style.background = "transparent";
|
|
7871
|
+
},
|
|
7872
|
+
children: "x"
|
|
7873
|
+
})]
|
|
7874
|
+
}), detailsExpanded && details && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7875
|
+
style: {
|
|
7876
|
+
marginTop: "10px",
|
|
7877
|
+
padding: "10px",
|
|
7878
|
+
background: "rgba(0, 0, 0, 0.04)",
|
|
7879
|
+
borderRadius: "6px",
|
|
7880
|
+
fontSize: "11px",
|
|
7881
|
+
fontFamily: "monospace",
|
|
7882
|
+
color: colors.text,
|
|
7883
|
+
lineHeight: "1.5",
|
|
7884
|
+
maxHeight: "200px",
|
|
7885
|
+
overflowY: "auto",
|
|
7886
|
+
whiteSpace: "pre-wrap",
|
|
7887
|
+
wordBreak: "break-all"
|
|
7888
|
+
},
|
|
7889
|
+
children: [
|
|
7890
|
+
details.code && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
7891
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Code:" }),
|
|
7892
|
+
" ",
|
|
7893
|
+
details.code
|
|
7894
|
+
] }),
|
|
7895
|
+
details.originalMessage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7896
|
+
style: { marginTop: "4px" },
|
|
7897
|
+
children: [
|
|
7898
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Message:" }),
|
|
7899
|
+
" ",
|
|
7900
|
+
details.originalMessage
|
|
7901
|
+
]
|
|
7902
|
+
}),
|
|
7903
|
+
details.context && Object.keys(details.context).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7904
|
+
style: { marginTop: "4px" },
|
|
7905
|
+
children: [
|
|
7906
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Context:" }),
|
|
7907
|
+
" ",
|
|
7908
|
+
JSON.stringify(details.context, null, 2)
|
|
7909
|
+
]
|
|
7910
|
+
}),
|
|
7911
|
+
details.stack && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7912
|
+
style: {
|
|
7913
|
+
marginTop: "4px",
|
|
7914
|
+
opacity: .7
|
|
7915
|
+
},
|
|
7916
|
+
children: [
|
|
7917
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Stack:" }),
|
|
7918
|
+
"\n",
|
|
7919
|
+
details.stack
|
|
7920
|
+
]
|
|
7921
|
+
})
|
|
7922
|
+
]
|
|
7923
|
+
})]
|
|
7924
|
+
});
|
|
7925
|
+
}
|
|
5875
7926
|
function ToastProvider({ enabled, children }) {
|
|
5876
7927
|
const [toasts, setToasts] = (0, react.useState)([]);
|
|
5877
7928
|
const [bannerError, setBannerErrorState] = (0, react.useState)(null);
|
|
@@ -5909,156 +7960,10 @@ function ToastProvider({ enabled, children }) {
|
|
|
5909
7960
|
};
|
|
5910
7961
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToastContext.Provider, {
|
|
5911
7962
|
value,
|
|
5912
|
-
children: [bannerError && ((
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
position: "fixed",
|
|
5917
|
-
bottom: "20px",
|
|
5918
|
-
left: "50%",
|
|
5919
|
-
transform: "translateX(-50%)",
|
|
5920
|
-
zIndex: 9999,
|
|
5921
|
-
backgroundColor: colors.background,
|
|
5922
|
-
border: `1px solid ${colors.border}`,
|
|
5923
|
-
borderLeft: `4px solid ${colors.border}`,
|
|
5924
|
-
borderRadius: "8px",
|
|
5925
|
-
padding: "12px 16px",
|
|
5926
|
-
fontSize: "13px",
|
|
5927
|
-
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
5928
|
-
backdropFilter: "blur(8px)",
|
|
5929
|
-
maxWidth: "min(90vw, 700px)",
|
|
5930
|
-
width: "100%",
|
|
5931
|
-
boxSizing: "border-box",
|
|
5932
|
-
overflow: "hidden"
|
|
5933
|
-
},
|
|
5934
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5935
|
-
style: {
|
|
5936
|
-
display: "flex",
|
|
5937
|
-
justifyContent: "space-between",
|
|
5938
|
-
alignItems: "center",
|
|
5939
|
-
gap: "10px"
|
|
5940
|
-
},
|
|
5941
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5942
|
-
style: {
|
|
5943
|
-
display: "flex",
|
|
5944
|
-
alignItems: "center",
|
|
5945
|
-
gap: "8px",
|
|
5946
|
-
flex: 1,
|
|
5947
|
-
minWidth: 0
|
|
5948
|
-
},
|
|
5949
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
5950
|
-
width: "12px",
|
|
5951
|
-
height: "12px",
|
|
5952
|
-
borderRadius: "50%",
|
|
5953
|
-
backgroundColor: colors.border,
|
|
5954
|
-
flexShrink: 0
|
|
5955
|
-
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5956
|
-
style: {
|
|
5957
|
-
display: "flex",
|
|
5958
|
-
alignItems: "center",
|
|
5959
|
-
gap: "10px",
|
|
5960
|
-
flex: 1,
|
|
5961
|
-
minWidth: 0
|
|
5962
|
-
},
|
|
5963
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5964
|
-
style: {
|
|
5965
|
-
color: colors.text,
|
|
5966
|
-
lineHeight: "1.4",
|
|
5967
|
-
fontWeight: "400",
|
|
5968
|
-
fontSize: "13px",
|
|
5969
|
-
flex: 1,
|
|
5970
|
-
wordBreak: "break-all",
|
|
5971
|
-
overflowWrap: "break-word",
|
|
5972
|
-
maxWidth: "550px",
|
|
5973
|
-
overflow: "hidden",
|
|
5974
|
-
display: "-webkit-box",
|
|
5975
|
-
WebkitLineClamp: 10,
|
|
5976
|
-
WebkitBoxOrient: "vertical"
|
|
5977
|
-
},
|
|
5978
|
-
children: (() => {
|
|
5979
|
-
let message = bannerError.message;
|
|
5980
|
-
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
|
|
5981
|
-
if (jsonMatch) return jsonMatch[1];
|
|
5982
|
-
message = message.split(" - ")[0];
|
|
5983
|
-
message = message.split(": Error code")[0];
|
|
5984
|
-
message = message.replace(/:\s*\d{3}$/, "");
|
|
5985
|
-
message = message.replace(/See more:.*$/g, "");
|
|
5986
|
-
message = message.trim();
|
|
5987
|
-
return message || "Configuration error occurred.";
|
|
5988
|
-
})()
|
|
5989
|
-
}), (() => {
|
|
5990
|
-
const message = bannerError.message;
|
|
5991
|
-
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
5992
|
-
const plainUrlRegex = /(https?:\/\/[^\s)]+)/g;
|
|
5993
|
-
let url = null;
|
|
5994
|
-
let buttonText = "See More";
|
|
5995
|
-
const markdownMatch = markdownLinkRegex.exec(message);
|
|
5996
|
-
if (markdownMatch) {
|
|
5997
|
-
url = markdownMatch[2];
|
|
5998
|
-
buttonText = "See More";
|
|
5999
|
-
} else {
|
|
6000
|
-
const urlMatch = plainUrlRegex.exec(message);
|
|
6001
|
-
if (urlMatch) {
|
|
6002
|
-
url = urlMatch[0].replace(/[.,;:'"]*$/, "");
|
|
6003
|
-
buttonText = "See More";
|
|
6004
|
-
}
|
|
6005
|
-
}
|
|
6006
|
-
if (!url) return null;
|
|
6007
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6008
|
-
onClick: () => window.open(url, "_blank", "noopener,noreferrer"),
|
|
6009
|
-
style: {
|
|
6010
|
-
background: colors.border,
|
|
6011
|
-
color: "white",
|
|
6012
|
-
border: "none",
|
|
6013
|
-
borderRadius: "5px",
|
|
6014
|
-
padding: "4px 10px",
|
|
6015
|
-
fontSize: "11px",
|
|
6016
|
-
fontWeight: "500",
|
|
6017
|
-
cursor: "pointer",
|
|
6018
|
-
transition: "all 0.2s ease",
|
|
6019
|
-
flexShrink: 0
|
|
6020
|
-
},
|
|
6021
|
-
onMouseEnter: (e) => {
|
|
6022
|
-
e.currentTarget.style.opacity = "0.9";
|
|
6023
|
-
e.currentTarget.style.transform = "translateY(-1px)";
|
|
6024
|
-
},
|
|
6025
|
-
onMouseLeave: (e) => {
|
|
6026
|
-
e.currentTarget.style.opacity = "1";
|
|
6027
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
6028
|
-
},
|
|
6029
|
-
children: buttonText
|
|
6030
|
-
});
|
|
6031
|
-
})()]
|
|
6032
|
-
})]
|
|
6033
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6034
|
-
onClick: () => setBannerError(null),
|
|
6035
|
-
style: {
|
|
6036
|
-
background: "transparent",
|
|
6037
|
-
border: "none",
|
|
6038
|
-
color: colors.text,
|
|
6039
|
-
cursor: "pointer",
|
|
6040
|
-
padding: "2px",
|
|
6041
|
-
borderRadius: "3px",
|
|
6042
|
-
fontSize: "14px",
|
|
6043
|
-
lineHeight: "1",
|
|
6044
|
-
opacity: .6,
|
|
6045
|
-
transition: "all 0.2s ease",
|
|
6046
|
-
flexShrink: 0
|
|
6047
|
-
},
|
|
6048
|
-
title: "Dismiss",
|
|
6049
|
-
onMouseEnter: (e) => {
|
|
6050
|
-
e.currentTarget.style.opacity = "1";
|
|
6051
|
-
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
6052
|
-
},
|
|
6053
|
-
onMouseLeave: (e) => {
|
|
6054
|
-
e.currentTarget.style.opacity = "0.6";
|
|
6055
|
-
e.currentTarget.style.background = "transparent";
|
|
6056
|
-
},
|
|
6057
|
-
children: "×"
|
|
6058
|
-
})]
|
|
6059
|
-
})
|
|
6060
|
-
});
|
|
6061
|
-
})(), children]
|
|
7963
|
+
children: [bannerError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerErrorDisplay, {
|
|
7964
|
+
bannerError,
|
|
7965
|
+
onDismiss: () => setBannerError(null)
|
|
7966
|
+
}), children]
|
|
6062
7967
|
});
|
|
6063
7968
|
}
|
|
6064
7969
|
|
|
@@ -7065,12 +8970,21 @@ function CopilotListeners() {
|
|
|
7065
8970
|
const { agent } = useAgent({ agentId: resolvedAgentId });
|
|
7066
8971
|
usePredictStateSubscription(agent);
|
|
7067
8972
|
(0, react.useEffect)(() => {
|
|
7068
|
-
const subscription = copilotkit.subscribe({ onError: ({ error }) => {
|
|
7069
|
-
|
|
8973
|
+
const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
|
|
8974
|
+
if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
|
|
8975
|
+
if (process.env.NODE_ENV === "development") console.error("[CopilotKit] Agent error:", error.message, "\n Code:", code, "\n Context:", context, "\n Stack:", error.stack);
|
|
8976
|
+
const ckError = new _copilotkit_shared.CopilotKitLowLevelError({
|
|
7070
8977
|
error,
|
|
7071
8978
|
message: error.message,
|
|
7072
8979
|
url: typeof window !== "undefined" ? window.location.href : ""
|
|
7073
|
-
})
|
|
8980
|
+
});
|
|
8981
|
+
ckError.details = {
|
|
8982
|
+
code,
|
|
8983
|
+
context,
|
|
8984
|
+
stack: error.stack,
|
|
8985
|
+
originalMessage: error.message
|
|
8986
|
+
};
|
|
8987
|
+
setBannerError(ckError);
|
|
7074
8988
|
} });
|
|
7075
8989
|
return () => {
|
|
7076
8990
|
subscription.unsubscribe();
|
|
@@ -7544,7 +9458,7 @@ function CopilotKitInternal(cpkProps) {
|
|
|
7544
9458
|
children: [
|
|
7545
9459
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotListeners, {}),
|
|
7546
9460
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitErrorBridge, {}),
|
|
7547
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [memoizedChildren, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {})] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
|
|
9461
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: memoizedChildren }, "children"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {}, "actions")] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
|
|
7548
9462
|
severity: bannerError.severity,
|
|
7549
9463
|
message: bannerError.message,
|
|
7550
9464
|
onClose: () => setBannerError(null),
|
|
@@ -7624,6 +9538,18 @@ Object.defineProperty(exports, 'CopilotChatAssistantMessage_default', {
|
|
|
7624
9538
|
return CopilotChatAssistantMessage_default;
|
|
7625
9539
|
}
|
|
7626
9540
|
});
|
|
9541
|
+
Object.defineProperty(exports, 'CopilotChatAttachmentQueue', {
|
|
9542
|
+
enumerable: true,
|
|
9543
|
+
get: function () {
|
|
9544
|
+
return CopilotChatAttachmentQueue;
|
|
9545
|
+
}
|
|
9546
|
+
});
|
|
9547
|
+
Object.defineProperty(exports, 'CopilotChatAttachmentRenderer', {
|
|
9548
|
+
enumerable: true,
|
|
9549
|
+
get: function () {
|
|
9550
|
+
return CopilotChatAttachmentRenderer;
|
|
9551
|
+
}
|
|
9552
|
+
});
|
|
7627
9553
|
Object.defineProperty(exports, 'CopilotChatAudioRecorder', {
|
|
7628
9554
|
enumerable: true,
|
|
7629
9555
|
get: function () {
|
|
@@ -7786,6 +9712,12 @@ Object.defineProperty(exports, 'MCPAppsActivityType', {
|
|
|
7786
9712
|
return MCPAppsActivityType;
|
|
7787
9713
|
}
|
|
7788
9714
|
});
|
|
9715
|
+
Object.defineProperty(exports, 'SandboxFunctionsContext', {
|
|
9716
|
+
enumerable: true,
|
|
9717
|
+
get: function () {
|
|
9718
|
+
return SandboxFunctionsContext;
|
|
9719
|
+
}
|
|
9720
|
+
});
|
|
7789
9721
|
Object.defineProperty(exports, 'ThreadsContext', {
|
|
7790
9722
|
enumerable: true,
|
|
7791
9723
|
get: function () {
|
|
@@ -7858,6 +9790,12 @@ Object.defineProperty(exports, 'useAsyncCallback', {
|
|
|
7858
9790
|
return useAsyncCallback;
|
|
7859
9791
|
}
|
|
7860
9792
|
});
|
|
9793
|
+
Object.defineProperty(exports, 'useAttachments', {
|
|
9794
|
+
enumerable: true,
|
|
9795
|
+
get: function () {
|
|
9796
|
+
return useAttachments;
|
|
9797
|
+
}
|
|
9798
|
+
});
|
|
7861
9799
|
Object.defineProperty(exports, 'useCoAgentStateRenders', {
|
|
7862
9800
|
enumerable: true,
|
|
7863
9801
|
get: function () {
|
|
@@ -7948,6 +9886,12 @@ Object.defineProperty(exports, 'useRenderToolCall', {
|
|
|
7948
9886
|
return useRenderToolCall;
|
|
7949
9887
|
}
|
|
7950
9888
|
});
|
|
9889
|
+
Object.defineProperty(exports, 'useSandboxFunctions', {
|
|
9890
|
+
enumerable: true,
|
|
9891
|
+
get: function () {
|
|
9892
|
+
return useSandboxFunctions;
|
|
9893
|
+
}
|
|
9894
|
+
});
|
|
7951
9895
|
Object.defineProperty(exports, 'useSuggestions', {
|
|
7952
9896
|
enumerable: true,
|
|
7953
9897
|
get: function () {
|
|
@@ -7972,4 +9916,4 @@ Object.defineProperty(exports, 'useToast', {
|
|
|
7972
9916
|
return useToast;
|
|
7973
9917
|
}
|
|
7974
9918
|
});
|
|
7975
|
-
//# sourceMappingURL=copilotkit-
|
|
9919
|
+
//# sourceMappingURL=copilotkit-Bz5-ImDl.cjs.map
|