@copilotkit/react-core 1.56.2 → 1.56.4-canary.1777529757
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/dist/{copilotkit-CSJw5BG8.cjs → copilotkit-BAkj3zUc.cjs} +359 -157
- package/dist/copilotkit-BAkj3zUc.cjs.map +1 -0
- package/dist/{copilotkit-Cj2ZIxVr.mjs → copilotkit-DAatqMh2.mjs} +360 -158
- package/dist/copilotkit-DAatqMh2.mjs.map +1 -0
- package/dist/{copilotkit-CCbxm6JM.d.mts → copilotkit-DFaI4j2r.d.mts} +64 -18
- package/dist/copilotkit-DFaI4j2r.d.mts.map +1 -0
- package/dist/{copilotkit-BtP7w7cT.d.cts → copilotkit-Dg4r4Gi_.d.cts} +64 -18
- package/dist/copilotkit-Dg4r4Gi_.d.cts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.umd.js +31 -44
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +361 -163
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -8
- package/src/components/copilot-provider/__tests__/v1-explicit-threadid-bridge.test.tsx +107 -0
- package/src/components/copilot-provider/copilotkit.tsx +6 -1
- package/src/context/__tests__/threads-context.test.tsx +116 -3
- package/src/context/threads-context.tsx +18 -1
- package/src/v2/components/chat/CopilotChat.tsx +91 -4
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +7 -114
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +26 -6
- package/src/v2/components/chat/CopilotChatInput.tsx +22 -0
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +2 -2
- package/src/v2/components/chat/CopilotChatView.tsx +226 -48
- package/src/v2/components/chat/Lightbox.tsx +103 -0
- package/src/v2/components/chat/__tests__/CopilotChat.absentThreadConnect.test.tsx +66 -0
- package/src/v2/components/chat/__tests__/CopilotChat.suggestionsAlways.test.tsx +189 -0
- package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +186 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +438 -4
- package/src/v2/components/chat/__tests__/CopilotChatView.connectingGate.test.tsx +56 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.tsx +264 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx +94 -0
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +0 -1
- package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +37 -0
- package/src/v2/components/chat/index.ts +2 -0
- package/src/v2/components/chat/last-user-message-context.ts +21 -0
- package/src/v2/components/chat/normalize-auto-scroll.ts +17 -0
- package/src/v2/components/license-warning-banner.tsx +20 -1
- package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +6 -0
- package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +6 -0
- package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +76 -50
- package/src/v2/hooks/__tests__/use-pin-to-send.test.tsx +219 -0
- package/src/v2/hooks/__tests__/use-threads.test.tsx +68 -0
- package/src/v2/hooks/use-agent.tsx +34 -77
- package/src/v2/hooks/use-pin-to-send.ts +94 -0
- package/src/v2/hooks/use-threads.tsx +55 -12
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +29 -1
- package/src/v2/providers/CopilotKitProvider.tsx +2 -11
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +106 -0
- package/dist/copilotkit-BtP7w7cT.d.cts.map +0 -1
- package/dist/copilotkit-CCbxm6JM.d.mts.map +0 -1
- package/dist/copilotkit-CSJw5BG8.cjs.map +0 -1
- package/dist/copilotkit-Cj2ZIxVr.mjs.map +0 -1
|
@@ -45,8 +45,8 @@ 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
47
|
let zod_to_json_schema = require("zod-to-json-schema");
|
|
48
|
-
let _tanstack_react_virtual = require("@tanstack/react-virtual");
|
|
49
48
|
let react_dom = require("react-dom");
|
|
49
|
+
let _tanstack_react_virtual = require("@tanstack/react-virtual");
|
|
50
50
|
let use_stick_to_bottom = require("use-stick-to-bottom");
|
|
51
51
|
let react_markdown = require("react-markdown");
|
|
52
52
|
react_markdown = __toESM(react_markdown);
|
|
@@ -175,7 +175,7 @@ const CopilotChatDefaultLabels = {
|
|
|
175
175
|
welcomeMessageText: "How can I help you today?"
|
|
176
176
|
};
|
|
177
177
|
const CopilotChatConfiguration = (0, react.createContext)(null);
|
|
178
|
-
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
|
|
178
|
+
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
|
|
179
179
|
const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
|
|
180
180
|
const stableLabels = useShallowStableRef(labels);
|
|
181
181
|
const mergedLabels = (0, react.useMemo)(() => ({
|
|
@@ -189,6 +189,7 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
189
189
|
if (parentConfig?.threadId) return parentConfig.threadId;
|
|
190
190
|
return (0, _copilotkit_shared.randomUUID)();
|
|
191
191
|
}, [threadId, parentConfig?.threadId]);
|
|
192
|
+
const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
|
|
192
193
|
const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
|
|
193
194
|
const hasExplicitDefault = isModalDefaultOpen !== void 0;
|
|
194
195
|
const setAndSync = (0, react.useCallback)((open) => {
|
|
@@ -211,12 +212,14 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
211
212
|
labels: mergedLabels,
|
|
212
213
|
agentId: resolvedAgentId,
|
|
213
214
|
threadId: resolvedThreadId,
|
|
215
|
+
hasExplicitThreadId: resolvedHasExplicitThreadId,
|
|
214
216
|
isModalOpen: resolvedIsModalOpen,
|
|
215
217
|
setModalOpen: resolvedSetModalOpen
|
|
216
218
|
}), [
|
|
217
219
|
mergedLabels,
|
|
218
220
|
resolvedAgentId,
|
|
219
221
|
resolvedThreadId,
|
|
222
|
+
resolvedHasExplicitThreadId,
|
|
220
223
|
resolvedIsModalOpen,
|
|
221
224
|
resolvedSetModalOpen
|
|
222
225
|
]);
|
|
@@ -610,7 +613,7 @@ CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
|
|
|
610
613
|
//#region src/v2/components/chat/CopilotChatInput.tsx
|
|
611
614
|
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
|
|
612
615
|
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
|
|
613
|
-
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 }) {
|
|
616
|
+
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, bottomAnchored = false, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
|
|
614
617
|
const isControlled = value !== void 0;
|
|
615
618
|
const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
|
|
616
619
|
(0, react.useEffect)(() => {
|
|
@@ -1134,7 +1137,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1134
1137
|
className: cn("cpk:pointer-events-none cpk:relative cpk:z-20", positioning === "absolute" && "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0", className),
|
|
1135
1138
|
style: {
|
|
1136
1139
|
transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
|
|
1137
|
-
transition: "transform 0.2s ease-out"
|
|
1140
|
+
transition: "transform 0.2s ease-out",
|
|
1141
|
+
...positioning === "absolute" || bottomAnchored ? { paddingBottom: "var(--copilotkit-license-banner-offset, 0px)" } : {}
|
|
1138
1142
|
},
|
|
1139
1143
|
...props,
|
|
1140
1144
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -1349,6 +1353,8 @@ CopilotKitInspector.displayName = "CopilotKitInspector";
|
|
|
1349
1353
|
|
|
1350
1354
|
//#endregion
|
|
1351
1355
|
//#region src/v2/components/license-warning-banner.tsx
|
|
1356
|
+
const LICENSE_BANNER_OFFSET_PX = 52;
|
|
1357
|
+
const LICENSE_BANNER_OFFSET_VAR = "--copilotkit-license-banner-offset";
|
|
1352
1358
|
const BANNER_STYLES = {
|
|
1353
1359
|
base: {
|
|
1354
1360
|
position: "fixed",
|
|
@@ -1390,6 +1396,14 @@ function getSeverityStyle(severity) {
|
|
|
1390
1396
|
}
|
|
1391
1397
|
}
|
|
1392
1398
|
function BannerShell({ severity, message, actionLabel, actionUrl, onDismiss }) {
|
|
1399
|
+
(0, react.useEffect)(() => {
|
|
1400
|
+
if (typeof document === "undefined") return;
|
|
1401
|
+
const root = document.documentElement;
|
|
1402
|
+
root.style.setProperty(LICENSE_BANNER_OFFSET_VAR, `${LICENSE_BANNER_OFFSET_PX}px`);
|
|
1403
|
+
return () => {
|
|
1404
|
+
root.style.removeProperty(LICENSE_BANNER_OFFSET_VAR);
|
|
1405
|
+
};
|
|
1406
|
+
}, []);
|
|
1393
1407
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1394
1408
|
style: {
|
|
1395
1409
|
...BANNER_STYLES.base,
|
|
@@ -3342,7 +3356,6 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, c
|
|
|
3342
3356
|
didMountRef.current = true;
|
|
3343
3357
|
}, []);
|
|
3344
3358
|
(0, react.useEffect)(() => {
|
|
3345
|
-
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.`);
|
|
3346
3359
|
copilotkit.setDefaultThrottleMs(defaultThrottleMs);
|
|
3347
3360
|
}, [copilotkit, defaultThrottleMs]);
|
|
3348
3361
|
const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
|
|
@@ -3559,15 +3572,6 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
|
|
|
3559
3572
|
const providerThrottleMs = copilotkit.defaultThrottleMs;
|
|
3560
3573
|
const chatConfig = useCopilotChatConfiguration();
|
|
3561
3574
|
threadId ??= chatConfig?.threadId;
|
|
3562
|
-
const effectiveThrottleMs = (0, react.useMemo)(() => {
|
|
3563
|
-
const resolved = throttleMs ?? providerThrottleMs ?? 0;
|
|
3564
|
-
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
3565
|
-
const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
|
|
3566
|
-
console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
|
|
3567
|
-
return 0;
|
|
3568
|
-
}
|
|
3569
|
-
return resolved;
|
|
3570
|
-
}, [throttleMs, providerThrottleMs]);
|
|
3571
3575
|
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
3572
3576
|
const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
|
|
3573
3577
|
const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
@@ -3630,9 +3634,8 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
|
|
|
3630
3634
|
]);
|
|
3631
3635
|
(0, react.useEffect)(() => {
|
|
3632
3636
|
if (updateFlags.length === 0) return;
|
|
3633
|
-
const handlers = {};
|
|
3634
|
-
let timerId = null;
|
|
3635
3637
|
let active = true;
|
|
3638
|
+
const handlers = {};
|
|
3636
3639
|
let batchScheduled = false;
|
|
3637
3640
|
const batchedForceUpdate = () => {
|
|
3638
3641
|
if (!active) return;
|
|
@@ -3644,46 +3647,24 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
|
|
|
3644
3647
|
});
|
|
3645
3648
|
}
|
|
3646
3649
|
};
|
|
3647
|
-
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged))
|
|
3648
|
-
const ms = effectiveThrottleMs;
|
|
3649
|
-
if (ms > 0) {
|
|
3650
|
-
let throttleActive = false;
|
|
3651
|
-
let pending = false;
|
|
3652
|
-
const throttledNotify = () => {
|
|
3653
|
-
if (!active) return;
|
|
3654
|
-
if (!throttleActive) {
|
|
3655
|
-
throttleActive = true;
|
|
3656
|
-
pending = false;
|
|
3657
|
-
forceUpdate();
|
|
3658
|
-
timerId = setTimeout(function trailingEdge() {
|
|
3659
|
-
timerId = null;
|
|
3660
|
-
if (active && pending) {
|
|
3661
|
-
pending = false;
|
|
3662
|
-
forceUpdate();
|
|
3663
|
-
timerId = setTimeout(trailingEdge, ms);
|
|
3664
|
-
} else throttleActive = false;
|
|
3665
|
-
}, ms);
|
|
3666
|
-
} else pending = true;
|
|
3667
|
-
};
|
|
3668
|
-
handlers.onMessagesChanged = throttledNotify;
|
|
3669
|
-
} else handlers.onMessagesChanged = forceUpdate;
|
|
3670
|
-
}
|
|
3650
|
+
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = forceUpdate;
|
|
3671
3651
|
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
|
|
3672
3652
|
if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
|
|
3673
3653
|
handlers.onRunInitialized = batchedForceUpdate;
|
|
3674
3654
|
handlers.onRunFinalized = batchedForceUpdate;
|
|
3675
3655
|
handlers.onRunFailed = batchedForceUpdate;
|
|
3656
|
+
handlers.onRunErrorEvent = batchedForceUpdate;
|
|
3676
3657
|
}
|
|
3677
|
-
const subscription =
|
|
3658
|
+
const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
|
|
3678
3659
|
return () => {
|
|
3679
3660
|
active = false;
|
|
3680
|
-
if (timerId !== null) clearTimeout(timerId);
|
|
3681
3661
|
subscription.unsubscribe();
|
|
3682
3662
|
};
|
|
3683
3663
|
}, [
|
|
3684
3664
|
agent,
|
|
3685
3665
|
forceUpdate,
|
|
3686
|
-
|
|
3666
|
+
throttleMs,
|
|
3667
|
+
providerThrottleMs,
|
|
3687
3668
|
updateFlags
|
|
3688
3669
|
]);
|
|
3689
3670
|
(0, react.useEffect)(() => {
|
|
@@ -4637,13 +4618,14 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4637
4618
|
const { copilotkit } = useCopilotKit();
|
|
4638
4619
|
const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
|
|
4639
4620
|
const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
|
|
4640
|
-
const threads = (0, react.useMemo)(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
|
|
4621
|
+
const threads = (0, react.useMemo)(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt, lastRunAt }) => ({
|
|
4641
4622
|
id,
|
|
4642
4623
|
agentId,
|
|
4643
4624
|
name,
|
|
4644
4625
|
archived,
|
|
4645
4626
|
createdAt,
|
|
4646
|
-
updatedAt
|
|
4627
|
+
updatedAt,
|
|
4628
|
+
...lastRunAt !== void 0 ? { lastRunAt } : {}
|
|
4647
4629
|
})), [coreThreads]);
|
|
4648
4630
|
const storeIsLoading = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsIsLoading);
|
|
4649
4631
|
const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
|
|
@@ -4656,7 +4638,9 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4656
4638
|
if (copilotkit.runtimeUrl) return null;
|
|
4657
4639
|
return /* @__PURE__ */ new Error("Runtime URL is not configured");
|
|
4658
4640
|
}, [copilotkit.runtimeUrl]);
|
|
4659
|
-
const
|
|
4641
|
+
const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
|
|
4642
|
+
const preConnectLoading = !!copilotkit.runtimeUrl && !hasDispatchedContext;
|
|
4643
|
+
const isLoading = runtimeError ? false : preConnectLoading || storeIsLoading;
|
|
4660
4644
|
const error = runtimeError ?? storeError;
|
|
4661
4645
|
(0, react.useEffect)(() => {
|
|
4662
4646
|
store.start();
|
|
@@ -4664,19 +4648,27 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4664
4648
|
store.stop();
|
|
4665
4649
|
};
|
|
4666
4650
|
}, [store]);
|
|
4651
|
+
const runtimeStatus = copilotkit.runtimeConnectionStatus;
|
|
4667
4652
|
(0, react.useEffect)(() => {
|
|
4668
|
-
|
|
4653
|
+
if (!copilotkit.runtimeUrl) {
|
|
4654
|
+
store.setContext(null);
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4657
|
+
if (runtimeStatus !== _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connected) return;
|
|
4658
|
+
const context = {
|
|
4669
4659
|
runtimeUrl: copilotkit.runtimeUrl,
|
|
4670
4660
|
headers: { ...copilotkit.headers },
|
|
4671
4661
|
wsUrl: copilotkit.intelligence?.wsUrl,
|
|
4672
4662
|
agentId,
|
|
4673
4663
|
includeArchived,
|
|
4674
4664
|
limit
|
|
4675
|
-
}
|
|
4665
|
+
};
|
|
4676
4666
|
store.setContext(context);
|
|
4667
|
+
setHasDispatchedContext(true);
|
|
4677
4668
|
}, [
|
|
4678
4669
|
store,
|
|
4679
4670
|
copilotkit.runtimeUrl,
|
|
4671
|
+
runtimeStatus,
|
|
4680
4672
|
headersKey,
|
|
4681
4673
|
copilotkit.intelligence?.wsUrl,
|
|
4682
4674
|
agentId,
|
|
@@ -5044,20 +5036,101 @@ CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantM
|
|
|
5044
5036
|
CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
|
|
5045
5037
|
var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
|
|
5046
5038
|
|
|
5039
|
+
//#endregion
|
|
5040
|
+
//#region src/v2/components/chat/Lightbox.tsx
|
|
5041
|
+
function Lightbox({ onClose, children }) {
|
|
5042
|
+
(0, react.useEffect)(() => {
|
|
5043
|
+
const handleKey = (e) => {
|
|
5044
|
+
if (e.key === "Escape") onClose();
|
|
5045
|
+
};
|
|
5046
|
+
document.addEventListener("keydown", handleKey);
|
|
5047
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
5048
|
+
}, [onClose]);
|
|
5049
|
+
if (typeof document === "undefined") return null;
|
|
5050
|
+
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5051
|
+
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",
|
|
5052
|
+
onClick: onClose,
|
|
5053
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5054
|
+
onClick: onClose,
|
|
5055
|
+
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",
|
|
5056
|
+
"aria-label": "Close preview",
|
|
5057
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:w-5 cpk:h-5" })
|
|
5058
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5059
|
+
onClick: (e) => e.stopPropagation(),
|
|
5060
|
+
children
|
|
5061
|
+
})]
|
|
5062
|
+
}), document.body);
|
|
5063
|
+
}
|
|
5064
|
+
/**
|
|
5065
|
+
* Hook that manages lightbox open/close and uses the View Transition API to
|
|
5066
|
+
* morph the thumbnail into fullscreen content.
|
|
5067
|
+
*
|
|
5068
|
+
* The trick: `view-transition-name` must live on exactly ONE element at a time.
|
|
5069
|
+
* - Old state (thumbnail visible): name is on the thumbnail.
|
|
5070
|
+
* - New state (lightbox visible): name moves to the lightbox content.
|
|
5071
|
+
* `flushSync` ensures React commits the DOM change synchronously inside the
|
|
5072
|
+
* `startViewTransition` callback so the API can snapshot old → new correctly.
|
|
5073
|
+
*/
|
|
5074
|
+
function useLightbox() {
|
|
5075
|
+
const thumbnailRef = (0, react.useRef)(null);
|
|
5076
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
5077
|
+
const vtName = (0, react.useId)();
|
|
5078
|
+
return {
|
|
5079
|
+
thumbnailRef,
|
|
5080
|
+
vtName,
|
|
5081
|
+
open,
|
|
5082
|
+
openLightbox: (0, react.useCallback)(() => {
|
|
5083
|
+
const thumb = thumbnailRef.current;
|
|
5084
|
+
const doc = document;
|
|
5085
|
+
if (doc.startViewTransition && thumb) {
|
|
5086
|
+
thumb.style.viewTransitionName = vtName;
|
|
5087
|
+
doc.startViewTransition(() => {
|
|
5088
|
+
thumb.style.viewTransitionName = "";
|
|
5089
|
+
(0, react_dom.flushSync)(() => setOpen(true));
|
|
5090
|
+
});
|
|
5091
|
+
} else setOpen(true);
|
|
5092
|
+
}, [vtName]),
|
|
5093
|
+
closeLightbox: (0, react.useCallback)(() => {
|
|
5094
|
+
const thumb = thumbnailRef.current;
|
|
5095
|
+
const doc = document;
|
|
5096
|
+
if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
|
|
5097
|
+
(0, react_dom.flushSync)(() => setOpen(false));
|
|
5098
|
+
thumb.style.viewTransitionName = vtName;
|
|
5099
|
+
}).finished.then(() => {
|
|
5100
|
+
thumb.style.viewTransitionName = "";
|
|
5101
|
+
}).catch(() => {
|
|
5102
|
+
thumb.style.viewTransitionName = "";
|
|
5103
|
+
});
|
|
5104
|
+
else setOpen(false);
|
|
5105
|
+
}, [vtName])
|
|
5106
|
+
};
|
|
5107
|
+
}
|
|
5108
|
+
|
|
5047
5109
|
//#endregion
|
|
5048
5110
|
//#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
|
|
5049
5111
|
const ImageAttachment = (0, react.memo)(function ImageAttachment({ src, className }) {
|
|
5050
5112
|
const [error, setError] = (0, react.useState)(false);
|
|
5113
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5051
5114
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5052
5115
|
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),
|
|
5053
5116
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Failed to load image" })
|
|
5054
5117
|
});
|
|
5055
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5118
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5119
|
+
ref: thumbnailRef,
|
|
5056
5120
|
src,
|
|
5057
5121
|
alt: "Image attachment",
|
|
5058
|
-
className: cn("cpk:max-w-
|
|
5122
|
+
className: cn("cpk:max-w-[80px] cpk:max-h-[80px] cpk:w-auto cpk:h-auto cpk:rounded-xl cpk:object-cover cpk:cursor-pointer cpk:bg-muted", className),
|
|
5123
|
+
onClick: openLightbox,
|
|
5059
5124
|
onError: () => setError(true)
|
|
5060
|
-
})
|
|
5125
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
|
|
5126
|
+
onClose: closeLightbox,
|
|
5127
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5128
|
+
style: { viewTransitionName: vtName },
|
|
5129
|
+
src,
|
|
5130
|
+
alt: "Image attachment",
|
|
5131
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
|
|
5132
|
+
})
|
|
5133
|
+
})] });
|
|
5061
5134
|
});
|
|
5062
5135
|
const AudioAttachment = (0, react.memo)(function AudioAttachment({ src, filename, className }) {
|
|
5063
5136
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5182,15 +5255,15 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
5182
5255
|
"data-message-id": message.id,
|
|
5183
5256
|
...props,
|
|
5184
5257
|
children: [
|
|
5185
|
-
BoundMessageRenderer,
|
|
5186
5258
|
mediaParts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5187
|
-
className: "cpk:flex cpk:flex-
|
|
5259
|
+
className: "cpk:flex cpk:flex-row cpk:flex-wrap cpk:justify-end cpk:gap-2 cpk:mb-2",
|
|
5188
5260
|
children: mediaParts.map((part, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentRenderer, {
|
|
5189
5261
|
type: part.type,
|
|
5190
5262
|
source: part.source,
|
|
5191
5263
|
filename: getFilename(part)
|
|
5192
5264
|
}, index))
|
|
5193
5265
|
}),
|
|
5266
|
+
BoundMessageRenderer,
|
|
5194
5267
|
BoundToolbar
|
|
5195
5268
|
]
|
|
5196
5269
|
});
|
|
@@ -5859,6 +5932,7 @@ CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
|
5859
5932
|
const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
|
|
5860
5933
|
if (attachments.length === 0) return null;
|
|
5861
5934
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5935
|
+
"data-testid": "copilot-attachment-queue",
|
|
5862
5936
|
className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
|
|
5863
5937
|
children: attachments.map((attachment) => {
|
|
5864
5938
|
const isMedia = attachment.type === "image" || attachment.type === "video";
|
|
@@ -5893,73 +5967,6 @@ function AttachmentPreview({ attachment }) {
|
|
|
5893
5967
|
case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentPreview, { attachment });
|
|
5894
5968
|
}
|
|
5895
5969
|
}
|
|
5896
|
-
function Lightbox({ onClose, children }) {
|
|
5897
|
-
(0, react.useEffect)(() => {
|
|
5898
|
-
const handleKey = (e) => {
|
|
5899
|
-
if (e.key === "Escape") onClose();
|
|
5900
|
-
};
|
|
5901
|
-
document.addEventListener("keydown", handleKey);
|
|
5902
|
-
return () => document.removeEventListener("keydown", handleKey);
|
|
5903
|
-
}, [onClose]);
|
|
5904
|
-
if (typeof document === "undefined") return null;
|
|
5905
|
-
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5906
|
-
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",
|
|
5907
|
-
onClick: onClose,
|
|
5908
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5909
|
-
onClick: onClose,
|
|
5910
|
-
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",
|
|
5911
|
-
"aria-label": "Close preview",
|
|
5912
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:w-5 cpk:h-5" })
|
|
5913
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5914
|
-
onClick: (e) => e.stopPropagation(),
|
|
5915
|
-
children
|
|
5916
|
-
})]
|
|
5917
|
-
}), document.body);
|
|
5918
|
-
}
|
|
5919
|
-
/**
|
|
5920
|
-
* Hook that manages lightbox open/close and uses the View Transition API to
|
|
5921
|
-
* morph the thumbnail into fullscreen content.
|
|
5922
|
-
*
|
|
5923
|
-
* The trick: `view-transition-name` must live on exactly ONE element at a time.
|
|
5924
|
-
* - Old state (thumbnail visible): name is on the thumbnail.
|
|
5925
|
-
* - New state (lightbox visible): name moves to the lightbox content.
|
|
5926
|
-
* `flushSync` ensures React commits the DOM change synchronously inside the
|
|
5927
|
-
* `startViewTransition` callback so the API can snapshot old → new correctly.
|
|
5928
|
-
*/
|
|
5929
|
-
function useLightbox() {
|
|
5930
|
-
const thumbnailRef = (0, react.useRef)(null);
|
|
5931
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
5932
|
-
const vtName = (0, react.useId)();
|
|
5933
|
-
return {
|
|
5934
|
-
thumbnailRef,
|
|
5935
|
-
vtName,
|
|
5936
|
-
open,
|
|
5937
|
-
openLightbox: (0, react.useCallback)(() => {
|
|
5938
|
-
const thumb = thumbnailRef.current;
|
|
5939
|
-
const doc = document;
|
|
5940
|
-
if (doc.startViewTransition && thumb) {
|
|
5941
|
-
thumb.style.viewTransitionName = vtName;
|
|
5942
|
-
doc.startViewTransition(() => {
|
|
5943
|
-
thumb.style.viewTransitionName = "";
|
|
5944
|
-
(0, react_dom.flushSync)(() => setOpen(true));
|
|
5945
|
-
});
|
|
5946
|
-
} else setOpen(true);
|
|
5947
|
-
}, []),
|
|
5948
|
-
closeLightbox: (0, react.useCallback)(() => {
|
|
5949
|
-
const thumb = thumbnailRef.current;
|
|
5950
|
-
const doc = document;
|
|
5951
|
-
if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
|
|
5952
|
-
(0, react_dom.flushSync)(() => setOpen(false));
|
|
5953
|
-
thumb.style.viewTransitionName = vtName;
|
|
5954
|
-
}).finished.then(() => {
|
|
5955
|
-
thumb.style.viewTransitionName = "";
|
|
5956
|
-
}).catch(() => {
|
|
5957
|
-
thumb.style.viewTransitionName = "";
|
|
5958
|
-
});
|
|
5959
|
-
else setOpen(false);
|
|
5960
|
-
}, [])
|
|
5961
|
-
};
|
|
5962
|
-
}
|
|
5963
5970
|
function ImagePreview({ attachment }) {
|
|
5964
5971
|
const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
|
|
5965
5972
|
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
@@ -6201,9 +6208,91 @@ function useKeyboardHeight() {
|
|
|
6201
6208
|
return keyboardState;
|
|
6202
6209
|
}
|
|
6203
6210
|
|
|
6211
|
+
//#endregion
|
|
6212
|
+
//#region src/v2/components/chat/normalize-auto-scroll.ts
|
|
6213
|
+
const VALID = [
|
|
6214
|
+
"pin-to-bottom",
|
|
6215
|
+
"pin-to-send",
|
|
6216
|
+
"none"
|
|
6217
|
+
];
|
|
6218
|
+
function normalizeAutoScroll(value) {
|
|
6219
|
+
if (value === void 0) return "pin-to-bottom";
|
|
6220
|
+
if (value === true) return "pin-to-bottom";
|
|
6221
|
+
if (value === false) return "none";
|
|
6222
|
+
if (VALID.includes(value)) return value;
|
|
6223
|
+
return "pin-to-bottom";
|
|
6224
|
+
}
|
|
6225
|
+
|
|
6226
|
+
//#endregion
|
|
6227
|
+
//#region src/v2/components/chat/last-user-message-context.ts
|
|
6228
|
+
const LastUserMessageContext = react.default.createContext({
|
|
6229
|
+
id: null,
|
|
6230
|
+
sendNonce: 0
|
|
6231
|
+
});
|
|
6232
|
+
|
|
6233
|
+
//#endregion
|
|
6234
|
+
//#region src/v2/hooks/use-pin-to-send.ts
|
|
6235
|
+
function usePinToSend({ scrollRef, contentRef, spacerRef, topOffset = 16 }) {
|
|
6236
|
+
const { id, sendNonce } = (0, react.useContext)(LastUserMessageContext);
|
|
6237
|
+
const lastNonceRef = (0, react.useRef)(-1);
|
|
6238
|
+
const currentSpacerHeightRef = (0, react.useRef)(0);
|
|
6239
|
+
(0, react.useEffect)(() => {
|
|
6240
|
+
if (sendNonce === lastNonceRef.current) return;
|
|
6241
|
+
lastNonceRef.current = sendNonce;
|
|
6242
|
+
if (!id) return;
|
|
6243
|
+
const scrollEl = scrollRef.current;
|
|
6244
|
+
const contentEl = contentRef.current;
|
|
6245
|
+
const spacerEl = spacerRef.current;
|
|
6246
|
+
if (!scrollEl || !contentEl || !spacerEl) return;
|
|
6247
|
+
const escaped = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(id) : id.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
|
|
6248
|
+
const targetEl = contentEl.querySelector(`[data-message-id="${escaped}"]`);
|
|
6249
|
+
if (!targetEl) return;
|
|
6250
|
+
const viewportHeight = scrollEl.clientHeight;
|
|
6251
|
+
const userMessageHeight = targetEl.getBoundingClientRect().height;
|
|
6252
|
+
const paddingTop = parseFloat(getComputedStyle(targetEl).paddingTop) || 0;
|
|
6253
|
+
const bubbleHeight = Math.max(0, userMessageHeight - paddingTop);
|
|
6254
|
+
const spacerHeight = Math.max(0, viewportHeight - bubbleHeight - topOffset);
|
|
6255
|
+
spacerEl.style.height = `${spacerHeight}px`;
|
|
6256
|
+
currentSpacerHeightRef.current = spacerHeight;
|
|
6257
|
+
const raf = requestAnimationFrame(() => {
|
|
6258
|
+
const targetTop = computeOffsetTop(targetEl, scrollEl) + paddingTop - topOffset;
|
|
6259
|
+
scrollEl.scrollTo({
|
|
6260
|
+
top: Math.max(0, targetTop),
|
|
6261
|
+
behavior: "smooth"
|
|
6262
|
+
});
|
|
6263
|
+
});
|
|
6264
|
+
const ro = new ResizeObserver(() => {
|
|
6265
|
+
if (!contentEl || !spacerEl || !scrollEl) return;
|
|
6266
|
+
const consumedBelow = contentEl.getBoundingClientRect().height - computeOffsetTop(targetEl, contentEl) - userMessageHeight;
|
|
6267
|
+
const remaining = Math.max(0, spacerHeight - consumedBelow);
|
|
6268
|
+
if (remaining < currentSpacerHeightRef.current) {
|
|
6269
|
+
spacerEl.style.height = `${remaining}px`;
|
|
6270
|
+
currentSpacerHeightRef.current = remaining;
|
|
6271
|
+
}
|
|
6272
|
+
});
|
|
6273
|
+
ro.observe(contentEl);
|
|
6274
|
+
return () => {
|
|
6275
|
+
cancelAnimationFrame(raf);
|
|
6276
|
+
ro.disconnect();
|
|
6277
|
+
};
|
|
6278
|
+
}, [
|
|
6279
|
+
id,
|
|
6280
|
+
sendNonce,
|
|
6281
|
+
scrollRef,
|
|
6282
|
+
contentRef,
|
|
6283
|
+
spacerRef,
|
|
6284
|
+
topOffset
|
|
6285
|
+
]);
|
|
6286
|
+
}
|
|
6287
|
+
function computeOffsetTop(el, stopAt) {
|
|
6288
|
+
const elRect = el.getBoundingClientRect();
|
|
6289
|
+
const stopRect = stopAt.getBoundingClientRect();
|
|
6290
|
+
return elRect.top - stopRect.top + stopAt.scrollTop;
|
|
6291
|
+
}
|
|
6292
|
+
|
|
6204
6293
|
//#endregion
|
|
6205
6294
|
//#region src/v2/components/chat/CopilotChatView.tsx
|
|
6206
|
-
const
|
|
6295
|
+
const SCROLL_BUTTON_OFFSET = 16;
|
|
6207
6296
|
function DropOverlay() {
|
|
6208
6297
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6209
6298
|
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"),
|
|
@@ -6216,15 +6305,18 @@ function DropOverlay() {
|
|
|
6216
6305
|
})
|
|
6217
6306
|
});
|
|
6218
6307
|
}
|
|
6219
|
-
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 }) {
|
|
6220
|
-
const
|
|
6308
|
+
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, isConnecting = false, hasExplicitThreadId = false, disclaimer, children, className, ...props }) {
|
|
6309
|
+
const [inputContainerEl, setInputContainerEl] = (0, react.useState)(null);
|
|
6221
6310
|
const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
|
|
6222
6311
|
const [isResizing, setIsResizing] = (0, react.useState)(false);
|
|
6223
6312
|
const resizeTimeoutRef = (0, react.useRef)(null);
|
|
6224
6313
|
const { isKeyboardOpen, keyboardHeight, availableHeight } = useKeyboardHeight();
|
|
6225
6314
|
(0, react.useEffect)(() => {
|
|
6226
|
-
const element =
|
|
6227
|
-
if (!element)
|
|
6315
|
+
const element = inputContainerEl;
|
|
6316
|
+
if (!element) {
|
|
6317
|
+
setInputContainerHeight(0);
|
|
6318
|
+
return;
|
|
6319
|
+
}
|
|
6228
6320
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
6229
6321
|
for (const entry of entries) {
|
|
6230
6322
|
const newHeight = entry.contentRect.height;
|
|
@@ -6247,7 +6339,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6247
6339
|
resizeObserver.disconnect();
|
|
6248
6340
|
if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
|
|
6249
6341
|
};
|
|
6250
|
-
}, []);
|
|
6342
|
+
}, [inputContainerEl]);
|
|
6251
6343
|
const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, {
|
|
6252
6344
|
messages,
|
|
6253
6345
|
isRunning
|
|
@@ -6266,11 +6358,11 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6266
6358
|
onAddFile,
|
|
6267
6359
|
positioning: "static",
|
|
6268
6360
|
keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
|
|
6269
|
-
containerRef: inputContainerRef,
|
|
6270
6361
|
showDisclaimer: true,
|
|
6362
|
+
bottomAnchored: true,
|
|
6271
6363
|
...disclaimer !== void 0 ? { disclaimer } : {}
|
|
6272
6364
|
});
|
|
6273
|
-
const hasSuggestions = Array.isArray(suggestions) && suggestions.length > 0;
|
|
6365
|
+
const hasSuggestions = !isConnecting && Array.isArray(suggestions) && suggestions.length > 0;
|
|
6274
6366
|
const BoundSuggestionView = hasSuggestions ? renderSlot(suggestionView, CopilotChatSuggestionView, {
|
|
6275
6367
|
suggestions,
|
|
6276
6368
|
loadingIndexes: suggestionLoadingIndexes,
|
|
@@ -6282,7 +6374,8 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6282
6374
|
inputContainerHeight,
|
|
6283
6375
|
isResizing,
|
|
6284
6376
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6285
|
-
|
|
6377
|
+
"data-testid": "copilot-scroll-content",
|
|
6378
|
+
style: { paddingBottom: `${inputContainerHeight + (hasSuggestions ? 4 : 32)}px` },
|
|
6286
6379
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6287
6380
|
className: "cpk:max-w-3xl cpk:mx-auto",
|
|
6288
6381
|
children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -6292,7 +6385,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6292
6385
|
})
|
|
6293
6386
|
})
|
|
6294
6387
|
});
|
|
6295
|
-
if (messages.length === 0 && !(welcomeScreen === false)) {
|
|
6388
|
+
if (messages.length === 0 && !(welcomeScreen === false) && !isConnecting && !hasExplicitThreadId) {
|
|
6296
6389
|
const BoundInputForWelcome = renderSlot(input, CopilotChatInput_default, {
|
|
6297
6390
|
onSubmitMessage,
|
|
6298
6391
|
onStop,
|
|
@@ -6356,15 +6449,19 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6356
6449
|
children: [
|
|
6357
6450
|
dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
|
|
6358
6451
|
BoundScrollView,
|
|
6359
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
className: "cpk:
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6452
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6453
|
+
ref: setInputContainerEl,
|
|
6454
|
+
"data-testid": "copilot-input-overlay",
|
|
6455
|
+
className: "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0 cpk:z-20 cpk:pointer-events-none",
|
|
6456
|
+
children: [attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6457
|
+
className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full cpk:pointer-events-auto",
|
|
6458
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
|
|
6459
|
+
attachments,
|
|
6460
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6461
|
+
className: "cpk:px-4"
|
|
6462
|
+
})
|
|
6463
|
+
}), BoundInput]
|
|
6464
|
+
})
|
|
6368
6465
|
]
|
|
6369
6466
|
});
|
|
6370
6467
|
}
|
|
@@ -6393,15 +6490,66 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6393
6490
|
BoundFeather,
|
|
6394
6491
|
!isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6395
6492
|
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6396
|
-
style: { bottom: `${inputContainerHeight +
|
|
6493
|
+
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
|
|
6397
6494
|
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
6398
6495
|
})
|
|
6399
6496
|
] })
|
|
6400
6497
|
});
|
|
6401
6498
|
};
|
|
6402
|
-
|
|
6499
|
+
const PinToSendScrollContainer = ({ children, scrollRef, contentRef, scrollToBottom, scrollToBottomButton, feather, inputContainerHeight, isResizing, nonAutoScrollEl, nonAutoScrollRefCallback, showScrollButton, className, ...props }) => {
|
|
6500
|
+
const spacerRef = (0, react.useRef)(null);
|
|
6501
|
+
usePinToSend({
|
|
6502
|
+
scrollRef,
|
|
6503
|
+
contentRef,
|
|
6504
|
+
spacerRef,
|
|
6505
|
+
topOffset: 16
|
|
6506
|
+
});
|
|
6507
|
+
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
6508
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
|
|
6509
|
+
value: nonAutoScrollEl,
|
|
6510
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6511
|
+
className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:relative", className),
|
|
6512
|
+
children: [
|
|
6513
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6514
|
+
ref: nonAutoScrollRefCallback,
|
|
6515
|
+
className: "cpk:flex-1 cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
6516
|
+
...props,
|
|
6517
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6518
|
+
ref: contentRef,
|
|
6519
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6520
|
+
children
|
|
6521
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6522
|
+
ref: spacerRef,
|
|
6523
|
+
"data-pin-to-send-spacer": true,
|
|
6524
|
+
"aria-hidden": "true",
|
|
6525
|
+
style: {
|
|
6526
|
+
height: 0,
|
|
6527
|
+
flex: "0 0 auto"
|
|
6528
|
+
}
|
|
6529
|
+
})]
|
|
6530
|
+
}),
|
|
6531
|
+
BoundFeather,
|
|
6532
|
+
showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6533
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6534
|
+
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
|
|
6535
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
6536
|
+
})
|
|
6537
|
+
]
|
|
6538
|
+
})
|
|
6539
|
+
});
|
|
6540
|
+
};
|
|
6541
|
+
_CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
|
|
6542
|
+
const mode = normalizeAutoScroll(autoScroll);
|
|
6403
6543
|
const [hasMounted, setHasMounted] = (0, react.useState)(false);
|
|
6404
|
-
const
|
|
6544
|
+
const scrollRef = (0, react.useRef)(null);
|
|
6545
|
+
const contentRef = (0, react.useRef)(null);
|
|
6546
|
+
const scrollToBottom = (0, react.useCallback)(() => {
|
|
6547
|
+
const el = scrollRef.current;
|
|
6548
|
+
if (el) el.scrollTo({
|
|
6549
|
+
top: el.scrollHeight,
|
|
6550
|
+
behavior: "smooth"
|
|
6551
|
+
});
|
|
6552
|
+
}, []);
|
|
6405
6553
|
const [showScrollButton, setShowScrollButton] = (0, react.useState)(false);
|
|
6406
6554
|
const [nonAutoScrollEl, setNonAutoScrollEl] = (0, react.useState)(null);
|
|
6407
6555
|
const nonAutoScrollRefCallback = (0, react.useCallback)((el) => {
|
|
@@ -6412,7 +6560,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6412
6560
|
setHasMounted(true);
|
|
6413
6561
|
}, []);
|
|
6414
6562
|
(0, react.useEffect)(() => {
|
|
6415
|
-
if (
|
|
6563
|
+
if (mode === "pin-to-bottom") return;
|
|
6416
6564
|
const scrollElement = scrollRef.current;
|
|
6417
6565
|
if (!scrollElement) return;
|
|
6418
6566
|
const checkScroll = () => {
|
|
@@ -6426,7 +6574,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6426
6574
|
scrollElement.removeEventListener("scroll", checkScroll);
|
|
6427
6575
|
resizeObserver.disconnect();
|
|
6428
6576
|
};
|
|
6429
|
-
}, [scrollRef,
|
|
6577
|
+
}, [scrollRef, mode]);
|
|
6430
6578
|
if (!hasMounted) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6431
6579
|
className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
6432
6580
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -6434,7 +6582,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6434
6582
|
children
|
|
6435
6583
|
})
|
|
6436
6584
|
});
|
|
6437
|
-
if (
|
|
6585
|
+
if (mode === "none") {
|
|
6438
6586
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
6439
6587
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
|
|
6440
6588
|
value: nonAutoScrollEl,
|
|
@@ -6451,13 +6599,28 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6451
6599
|
BoundFeather,
|
|
6452
6600
|
showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6453
6601
|
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6454
|
-
style: { bottom: `${inputContainerHeight +
|
|
6602
|
+
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
|
|
6455
6603
|
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
6456
6604
|
})
|
|
6457
6605
|
]
|
|
6458
6606
|
})
|
|
6459
6607
|
});
|
|
6460
6608
|
}
|
|
6609
|
+
if (mode === "pin-to-send") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinToSendScrollContainer, {
|
|
6610
|
+
scrollRef,
|
|
6611
|
+
contentRef,
|
|
6612
|
+
scrollToBottom,
|
|
6613
|
+
scrollToBottomButton,
|
|
6614
|
+
feather,
|
|
6615
|
+
inputContainerHeight,
|
|
6616
|
+
isResizing,
|
|
6617
|
+
nonAutoScrollEl,
|
|
6618
|
+
nonAutoScrollRefCallback,
|
|
6619
|
+
showScrollButton,
|
|
6620
|
+
className,
|
|
6621
|
+
...props,
|
|
6622
|
+
children
|
|
6623
|
+
});
|
|
6461
6624
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom, {
|
|
6462
6625
|
className: cn("cpk:flex-1 cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0", className),
|
|
6463
6626
|
resize: "smooth",
|
|
@@ -6480,9 +6643,8 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6480
6643
|
...props,
|
|
6481
6644
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
|
|
6482
6645
|
});
|
|
6483
|
-
_CopilotChatView.Feather = ({ className,
|
|
6484
|
-
className
|
|
6485
|
-
style,
|
|
6646
|
+
_CopilotChatView.Feather = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6647
|
+
className,
|
|
6486
6648
|
...props
|
|
6487
6649
|
});
|
|
6488
6650
|
_CopilotChatView.WelcomeMessage = ({ className, ...props }) => {
|
|
@@ -6650,7 +6812,9 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
|
|
|
6650
6812
|
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
|
|
6651
6813
|
const existingConfig = useCopilotChatConfiguration();
|
|
6652
6814
|
const resolvedAgentId = agentId ?? existingConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
6653
|
-
const
|
|
6815
|
+
const providedThreadId = threadId ?? existingConfig?.threadId;
|
|
6816
|
+
const resolvedThreadId = (0, react.useMemo)(() => providedThreadId ?? (0, _copilotkit_shared.randomUUID)(), [providedThreadId]);
|
|
6817
|
+
const hasExplicitThreadId = !!threadId || !!existingConfig?.hasExplicitThreadId;
|
|
6654
6818
|
const { agent } = useAgent({
|
|
6655
6819
|
agentId: resolvedAgentId,
|
|
6656
6820
|
threadId: resolvedThreadId,
|
|
@@ -6688,7 +6852,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
6688
6852
|
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
6689
6853
|
const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
|
|
6690
6854
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
6855
|
+
const [lastConnectedThreadId, setLastConnectedThreadId] = (0, react.useState)(null);
|
|
6856
|
+
const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
|
|
6691
6857
|
(0, react.useEffect)(() => {
|
|
6858
|
+
if (!hasExplicitThreadId) return;
|
|
6692
6859
|
let detached = false;
|
|
6693
6860
|
const connectAbortController = new AbortController();
|
|
6694
6861
|
if (agent instanceof _ag_ui_client.HttpAgent) agent.abortController = connectAbortController;
|
|
@@ -6698,6 +6865,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
6698
6865
|
} catch (error) {
|
|
6699
6866
|
if (detached) return;
|
|
6700
6867
|
console.error("CopilotChat: connectAgent failed", error);
|
|
6868
|
+
} finally {
|
|
6869
|
+
if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
|
|
6870
|
+
if (!detached) setLastConnectedThreadId(resolvedThreadId);
|
|
6871
|
+
});
|
|
6701
6872
|
}
|
|
6702
6873
|
};
|
|
6703
6874
|
connect(agent);
|
|
@@ -6709,7 +6880,8 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
6709
6880
|
}, [
|
|
6710
6881
|
resolvedThreadId,
|
|
6711
6882
|
agent,
|
|
6712
|
-
resolvedAgentId
|
|
6883
|
+
resolvedAgentId,
|
|
6884
|
+
hasExplicitThreadId
|
|
6713
6885
|
]);
|
|
6714
6886
|
const onSubmitInput = (0, react.useCallback)(async (value) => {
|
|
6715
6887
|
if (selectedAttachments.some((a) => a.status === "uploading")) {
|
|
@@ -6862,6 +7034,22 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
6862
7034
|
const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
|
|
6863
7035
|
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
|
|
6864
7036
|
}).join(",")]);
|
|
7037
|
+
const lastUserMessageId = (0, react.useMemo)(() => {
|
|
7038
|
+
for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "user") return messages[i].id;
|
|
7039
|
+
return null;
|
|
7040
|
+
}, [messages]);
|
|
7041
|
+
const [sendNonce, setSendNonce] = (0, react.useState)(0);
|
|
7042
|
+
const prevLastUserMessageIdRef = (0, react.useRef)(lastUserMessageId);
|
|
7043
|
+
(0, react.useEffect)(() => {
|
|
7044
|
+
if (lastUserMessageId && lastUserMessageId !== prevLastUserMessageIdRef.current) {
|
|
7045
|
+
setSendNonce((n) => n + 1);
|
|
7046
|
+
prevLastUserMessageIdRef.current = lastUserMessageId;
|
|
7047
|
+
}
|
|
7048
|
+
}, [lastUserMessageId]);
|
|
7049
|
+
const lastUserMessageState = (0, react.useMemo)(() => ({
|
|
7050
|
+
id: lastUserMessageId,
|
|
7051
|
+
sendNonce
|
|
7052
|
+
}), [lastUserMessageId, sendNonce]);
|
|
6865
7053
|
const RenderedChatView = renderSlot(chatView, CopilotChatView, {
|
|
6866
7054
|
...mergedProps,
|
|
6867
7055
|
messages,
|
|
@@ -6880,11 +7068,14 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
6880
7068
|
dragOver,
|
|
6881
7069
|
onDragOver: handleDragOver,
|
|
6882
7070
|
onDragLeave: handleDragLeave,
|
|
6883
|
-
onDrop: handleDrop
|
|
7071
|
+
onDrop: handleDrop,
|
|
7072
|
+
isConnecting,
|
|
7073
|
+
hasExplicitThreadId
|
|
6884
7074
|
});
|
|
6885
7075
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
|
|
6886
7076
|
agentId: resolvedAgentId,
|
|
6887
7077
|
threadId: resolvedThreadId,
|
|
7078
|
+
hasExplicitThreadId,
|
|
6888
7079
|
labels,
|
|
6889
7080
|
isModalDefaultOpen,
|
|
6890
7081
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -6915,7 +7106,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
6915
7106
|
},
|
|
6916
7107
|
children: transcriptionError
|
|
6917
7108
|
}),
|
|
6918
|
-
|
|
7109
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LastUserMessageContext.Provider, {
|
|
7110
|
+
value: lastUserMessageState,
|
|
7111
|
+
children: RenderedChatView
|
|
7112
|
+
})
|
|
6919
7113
|
]
|
|
6920
7114
|
})
|
|
6921
7115
|
});
|
|
@@ -8641,12 +8835,19 @@ function useCoAgentStateRenders() {
|
|
|
8641
8835
|
//#region src/context/threads-context.tsx
|
|
8642
8836
|
const ThreadsContext = (0, react.createContext)(void 0);
|
|
8643
8837
|
function ThreadsProvider({ children, threadId: explicitThreadId }) {
|
|
8644
|
-
const [internalThreadId,
|
|
8838
|
+
const [internalThreadId, setInternalThreadId] = (0, react.useState)(() => (0, _copilotkit_shared.randomUUID)());
|
|
8839
|
+
const [internalIsExplicit, setInternalIsExplicit] = (0, react.useState)(false);
|
|
8645
8840
|
const threadId = explicitThreadId ?? internalThreadId;
|
|
8841
|
+
const isThreadIdExplicit = explicitThreadId != null || internalIsExplicit;
|
|
8842
|
+
const setThreadId = (0, react.useCallback)((value) => {
|
|
8843
|
+
setInternalThreadId(value);
|
|
8844
|
+
setInternalIsExplicit(true);
|
|
8845
|
+
}, []);
|
|
8646
8846
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadsContext.Provider, {
|
|
8647
8847
|
value: {
|
|
8648
8848
|
threadId,
|
|
8649
|
-
setThreadId
|
|
8849
|
+
setThreadId,
|
|
8850
|
+
isThreadIdExplicit
|
|
8650
8851
|
},
|
|
8651
8852
|
children
|
|
8652
8853
|
});
|
|
@@ -9348,7 +9549,7 @@ function CopilotKitInternal(cpkProps) {
|
|
|
9348
9549
|
if (props.agent) setAgentSession({ agentName: props.agent });
|
|
9349
9550
|
else setAgentSession(null);
|
|
9350
9551
|
}, [props.agent]);
|
|
9351
|
-
const { threadId, setThreadId: setInternalThreadId } = useThreads();
|
|
9552
|
+
const { threadId, setThreadId: setInternalThreadId, isThreadIdExplicit } = useThreads();
|
|
9352
9553
|
const setThreadId = (0, react.useCallback)((value) => {
|
|
9353
9554
|
if (props.threadId) throw new Error("Cannot call setThreadId() when threadId is provided via props.");
|
|
9354
9555
|
setInternalThreadId(value);
|
|
@@ -9548,6 +9749,7 @@ function CopilotKitInternal(cpkProps) {
|
|
|
9548
9749
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
|
|
9549
9750
|
agentId: props.agent ?? "default",
|
|
9550
9751
|
threadId,
|
|
9752
|
+
hasExplicitThreadId: isThreadIdExplicit,
|
|
9551
9753
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotContext.Provider, {
|
|
9552
9754
|
value: copilotContextValue,
|
|
9553
9755
|
children: [
|
|
@@ -10017,4 +10219,4 @@ Object.defineProperty(exports, 'useToast', {
|
|
|
10017
10219
|
return useToast;
|
|
10018
10220
|
}
|
|
10019
10221
|
});
|
|
10020
|
-
//# sourceMappingURL=copilotkit-
|
|
10222
|
+
//# sourceMappingURL=copilotkit-BAkj3zUc.cjs.map
|