@copilotkit/react-core 1.79.2-canary.1792172552 → 1.80.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/dist/{copilotkit-DcENKLnJ.mjs → copilotkit-BNLtgOyD.mjs} +1783 -238
- package/dist/copilotkit-BNLtgOyD.mjs.map +1 -0
- package/dist/{copilotkit-kcYN96tM.d.mts → copilotkit-CWOSXhRO.d.mts} +278 -41
- package/dist/copilotkit-CWOSXhRO.d.mts.map +1 -0
- package/dist/{copilotkit-ConxFSB2.cjs → copilotkit-DKICi3nC.cjs} +1764 -206
- package/dist/copilotkit-DKICi3nC.cjs.map +1 -0
- package/dist/{copilotkit-BA0vyDds.d.cts → copilotkit-Dfw9HzO9.d.cts} +278 -41
- package/dist/copilotkit-Dfw9HzO9.d.cts.map +1 -0
- package/dist/index.cjs +15 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +15 -3
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +48 -11
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/context.d.cts +7 -0
- package/dist/v2/context.d.cts.map +1 -1
- package/dist/v2/context.d.mts +7 -0
- package/dist/v2/context.d.mts.map +1 -1
- package/dist/v2/headless.cjs +23 -6
- package/dist/v2/headless.cjs.map +1 -1
- package/dist/v2/headless.d.cts +20 -2
- package/dist/v2/headless.d.cts.map +1 -1
- package/dist/v2/headless.d.mts +20 -2
- package/dist/v2/headless.d.mts.map +1 -1
- package/dist/v2/headless.mjs +23 -6
- package/dist/v2/headless.mjs.map +1 -1
- package/dist/v2/index.cjs +3 -7
- 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 +2 -2
- package/dist/v2/index.umd.js +1756 -215
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +14 -11
- package/dist/copilotkit-BA0vyDds.d.cts.map +0 -1
- package/dist/copilotkit-ConxFSB2.cjs.map +0 -1
- package/dist/copilotkit-DcENKLnJ.mjs.map +0 -1
- package/dist/copilotkit-kcYN96tM.d.mts.map +0 -1
|
@@ -47,6 +47,8 @@ let zod = require("zod");
|
|
|
47
47
|
let _copilotkit_mcp_apps_renderer_activity = require("@copilotkit/mcp-apps-renderer/activity");
|
|
48
48
|
let _copilotkit_a2ui_renderer = require("@copilotkit/a2ui-renderer");
|
|
49
49
|
let zod_to_json_schema = require("zod-to-json-schema");
|
|
50
|
+
let _radix_ui_react_popover = require("@radix-ui/react-popover");
|
|
51
|
+
_radix_ui_react_popover = __toESM(_radix_ui_react_popover);
|
|
50
52
|
let react_dom = require("react-dom");
|
|
51
53
|
let _tanstack_react_virtual = require("@tanstack/react-virtual");
|
|
52
54
|
let use_stick_to_bottom = require("use-stick-to-bottom");
|
|
@@ -104,6 +106,8 @@ const CopilotChatDefaultLabels = {
|
|
|
104
106
|
chatInputToolbarFinishTranscribeButtonLabel: "Finish",
|
|
105
107
|
chatInputToolbarAddButtonLabel: "Add attachments",
|
|
106
108
|
chatInputToolbarToolsButtonLabel: "Tools",
|
|
109
|
+
chatInputToolbarSendButtonLabel: "Send message",
|
|
110
|
+
chatInputToolbarStopButtonLabel: "Stop",
|
|
107
111
|
assistantMessageToolbarCopyCodeLabel: "Copy",
|
|
108
112
|
assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
|
|
109
113
|
assistantMessageToolbarCopyMessageLabel: "Copy",
|
|
@@ -116,13 +120,18 @@ const CopilotChatDefaultLabels = {
|
|
|
116
120
|
assistantMessageToolbarInspectorHideDescription: "Until you reload this page",
|
|
117
121
|
assistantMessageToolbarThumbsUpLabel: "Good response",
|
|
118
122
|
assistantMessageToolbarThumbsDownLabel: "Bad response",
|
|
123
|
+
assistantMessageFeedbackCommentPlaceholder: "Add a comment (optional)",
|
|
124
|
+
assistantMessageFeedbackCommentSubmitLabel: "Send comment",
|
|
119
125
|
assistantMessageToolbarReadAloudLabel: "Read aloud",
|
|
120
126
|
assistantMessageToolbarRegenerateLabel: "Regenerate",
|
|
121
127
|
userMessageToolbarCopyMessageLabel: "Copy",
|
|
122
128
|
userMessageToolbarEditMessageLabel: "Edit",
|
|
129
|
+
userMessageToolbarPreviousBranchLabel: "Previous branch",
|
|
130
|
+
userMessageToolbarNextBranchLabel: "Next branch",
|
|
123
131
|
chatDisclaimerText: "AI can make mistakes. Please verify important information.",
|
|
124
132
|
chatToggleOpenLabel: "Open chat",
|
|
125
133
|
chatToggleCloseLabel: "Close chat",
|
|
134
|
+
chatScrollToBottomLabel: "Scroll to bottom",
|
|
126
135
|
modalHeaderTitle: "CopilotKit Chat",
|
|
127
136
|
welcomeMessageText: "How can I help you today?"
|
|
128
137
|
};
|
|
@@ -384,9 +393,9 @@ ControlledModalOpenScope.displayName = "ControlledModalOpenScope";
|
|
|
384
393
|
|
|
385
394
|
//#endregion
|
|
386
395
|
//#region src/v2/lib/utils.ts
|
|
387
|
-
const twMerge$
|
|
396
|
+
const twMerge$12 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
|
|
388
397
|
function cn(...inputs) {
|
|
389
|
-
return twMerge$
|
|
398
|
+
return twMerge$12((0, clsx.clsx)(inputs));
|
|
390
399
|
}
|
|
391
400
|
|
|
392
401
|
//#endregion
|
|
@@ -834,6 +843,11 @@ function renderSlot(slot, DefaultComponent, props) {
|
|
|
834
843
|
//#region src/v2/components/chat/CopilotChatInput.tsx
|
|
835
844
|
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
|
|
836
845
|
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
|
|
846
|
+
/**
|
|
847
|
+
* The chat composer: a text area with the add-menu, voice and send/stop
|
|
848
|
+
* buttons, and a slash-command menu that lists `toolsMenu` items once the text
|
|
849
|
+
* starts with "/".
|
|
850
|
+
*/
|
|
837
851
|
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 }) {
|
|
838
852
|
const isControlled = value !== void 0;
|
|
839
853
|
const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
|
|
@@ -1053,10 +1067,12 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1053
1067
|
send();
|
|
1054
1068
|
};
|
|
1055
1069
|
const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, { ref: audioRecorderRef });
|
|
1070
|
+
const showsStop = isProcessing && canStop;
|
|
1056
1071
|
const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, {
|
|
1057
1072
|
onClick: handleSendButtonClick,
|
|
1058
1073
|
disabled: isProcessing ? !canStop : !canSend,
|
|
1059
|
-
|
|
1074
|
+
"aria-label": showsStop ? labels.chatInputToolbarStopButtonLabel : labels.chatInputToolbarSendButtonLabel,
|
|
1075
|
+
children: showsStop ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
|
|
1060
1076
|
});
|
|
1061
1077
|
const BoundStartTranscribeButton = renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { onClick: onStartTranscribe });
|
|
1062
1078
|
const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { onClick: onCancelTranscribe });
|
|
@@ -1368,18 +1384,22 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1368
1384
|
});
|
|
1369
1385
|
}
|
|
1370
1386
|
(function(_CopilotChatInput) {
|
|
1371
|
-
_CopilotChatInput.SendButton = ({ className, children, ...props }) =>
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1387
|
+
_CopilotChatInput.SendButton = ({ className, children, ...props }) => {
|
|
1388
|
+
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1389
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1390
|
+
className: "cpk:mr-[10px]",
|
|
1391
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1392
|
+
type: "button",
|
|
1393
|
+
"data-testid": "copilot-send-button",
|
|
1394
|
+
variant: "chatInputToolbarPrimary",
|
|
1395
|
+
size: "chatInputToolbarIcon",
|
|
1396
|
+
className,
|
|
1397
|
+
"aria-label": labels.chatInputToolbarSendButtonLabel,
|
|
1398
|
+
...props,
|
|
1399
|
+
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[18px]" })
|
|
1400
|
+
})
|
|
1401
|
+
});
|
|
1402
|
+
};
|
|
1383
1403
|
const ToolbarButton = _CopilotChatInput.ToolbarButton = ({ icon, labelKey, defaultClassName, className, ...props }) => {
|
|
1384
1404
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1385
1405
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
|
|
@@ -1389,6 +1409,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1389
1409
|
variant: "chatInputToolbarSecondary",
|
|
1390
1410
|
size: "chatInputToolbarIcon",
|
|
1391
1411
|
className: (0, tailwind_merge.twMerge)(defaultClassName, className),
|
|
1412
|
+
"aria-label": labels[labelKey],
|
|
1392
1413
|
...props,
|
|
1393
1414
|
children: icon
|
|
1394
1415
|
})
|
|
@@ -1459,6 +1480,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1459
1480
|
size: "chatInputToolbarIcon",
|
|
1460
1481
|
className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
|
|
1461
1482
|
disabled: isDisabled,
|
|
1483
|
+
"aria-label": labels.chatInputToolbarAddButtonLabel,
|
|
1462
1484
|
...props,
|
|
1463
1485
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
|
|
1464
1486
|
});
|
|
@@ -1608,6 +1630,32 @@ function warnAboutUnrenderedToolCalls(toolNames, registered, alreadyWarned) {
|
|
|
1608
1630
|
}
|
|
1609
1631
|
}
|
|
1610
1632
|
/**
|
|
1633
|
+
* The tool-call renderers registered on the provider, kept current.
|
|
1634
|
+
* @internal
|
|
1635
|
+
*/
|
|
1636
|
+
function useToolCallRenderers() {
|
|
1637
|
+
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
|
|
1638
|
+
return (0, react.useSyncExternalStore)((callback) => copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Which tool names have a renderer of their own, whether a wildcard (`"*"`)
|
|
1642
|
+
* renderer is registered, and which of them opted into grouping.
|
|
1643
|
+
* @internal
|
|
1644
|
+
*/
|
|
1645
|
+
function useToolCallRendererNames() {
|
|
1646
|
+
const renderers = useToolCallRenderers();
|
|
1647
|
+
return (0, react.useMemo)(() => {
|
|
1648
|
+
const named = renderers.filter((rc) => rc.name !== "*");
|
|
1649
|
+
const wildcards = renderers.filter((rc) => rc.name === "*");
|
|
1650
|
+
return {
|
|
1651
|
+
named: new Set(named.map((rc) => rc.name)),
|
|
1652
|
+
groupedNames: new Set(named.filter((rc) => rc.group).map((rc) => rc.name)),
|
|
1653
|
+
wildcard: wildcards.length > 0,
|
|
1654
|
+
wildcardGroups: wildcards.some((rc) => rc.group)
|
|
1655
|
+
};
|
|
1656
|
+
}, [renderers]);
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1611
1659
|
* Hook that returns a function to render tool calls based on the render functions
|
|
1612
1660
|
* defined in CopilotKitProvider.
|
|
1613
1661
|
*
|
|
@@ -1618,9 +1666,7 @@ function useRenderToolCall() {
|
|
|
1618
1666
|
const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
1619
1667
|
const unrenderedToolNames = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
1620
1668
|
const warnedToolNames = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
1621
|
-
const renderToolCalls = (
|
|
1622
|
-
return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
|
|
1623
|
-
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
1669
|
+
const renderToolCalls = useToolCallRenderers();
|
|
1624
1670
|
const renderToolCall = (0, react.useCallback)(({ toolCall, toolMessage }) => {
|
|
1625
1671
|
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
1626
1672
|
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
@@ -1655,7 +1701,7 @@ function useRenderToolCall() {
|
|
|
1655
1701
|
|
|
1656
1702
|
//#endregion
|
|
1657
1703
|
//#region package.json
|
|
1658
|
-
var version = "1.
|
|
1704
|
+
var version = "1.80.0";
|
|
1659
1705
|
|
|
1660
1706
|
//#endregion
|
|
1661
1707
|
//#region src/v2/components/CopilotKitInspector.tsx
|
|
@@ -1891,7 +1937,7 @@ function InlineFeatureWarning({ featureName }) {
|
|
|
1891
1937
|
* Fetches resource content on-demand via proxied MCP requests. The React shell
|
|
1892
1938
|
* owns the iframe; `bindMcpApp` owns the protocol.
|
|
1893
1939
|
*/
|
|
1894
|
-
const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, message, agent }) {
|
|
1940
|
+
const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, message, agent, allowSameOriginFallback }) {
|
|
1895
1941
|
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
|
|
1896
1942
|
const messageId = message?.id;
|
|
1897
1943
|
const containerRef = (0, react.useRef)(null);
|
|
@@ -1941,6 +1987,7 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, mess
|
|
|
1941
1987
|
getAgent: () => agentRef.current,
|
|
1942
1988
|
host: copilotkit,
|
|
1943
1989
|
messageId,
|
|
1990
|
+
options: { allowSameOriginFallback },
|
|
1944
1991
|
hooks: {
|
|
1945
1992
|
onResource: (resource) => {
|
|
1946
1993
|
if (!mounted) return;
|
|
@@ -1982,6 +2029,7 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, mess
|
|
|
1982
2029
|
agent,
|
|
1983
2030
|
copilotkit,
|
|
1984
2031
|
messageId,
|
|
2032
|
+
allowSameOriginFallback,
|
|
1985
2033
|
content.resourceUri,
|
|
1986
2034
|
content.serverHash,
|
|
1987
2035
|
content.serverId
|
|
@@ -3196,7 +3244,8 @@ function defineToolCallRenderer(def) {
|
|
|
3196
3244
|
name: def.name,
|
|
3197
3245
|
args: argsSchema,
|
|
3198
3246
|
render: def.render,
|
|
3199
|
-
...def.agentId ? { agentId: def.agentId } : {}
|
|
3247
|
+
...def.agentId ? { agentId: def.agentId } : {},
|
|
3248
|
+
...def.group ? { group: true } : {}
|
|
3200
3249
|
};
|
|
3201
3250
|
}
|
|
3202
3251
|
|
|
@@ -3412,6 +3461,11 @@ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follo
|
|
|
3412
3461
|
- Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
|
|
3413
3462
|
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
|
|
3414
3463
|
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
|
|
3464
|
+
/** The MCP Apps renderer with `mcpApps.allowSameOriginFallback` applied. */
|
|
3465
|
+
const SameOriginMCPAppsActivityRenderer = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MCPAppsActivityRenderer, {
|
|
3466
|
+
...props,
|
|
3467
|
+
allowSameOriginFallback: true
|
|
3468
|
+
});
|
|
3415
3469
|
const DEFAULT_LEARNING = Object.freeze({});
|
|
3416
3470
|
function toLearningConfig(learning) {
|
|
3417
3471
|
if (learning === true) return DEFAULT_LEARNING;
|
|
@@ -3428,7 +3482,8 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
|
|
|
3428
3482
|
}, [value, warningMessage]);
|
|
3429
3483
|
return value;
|
|
3430
3484
|
}
|
|
3431
|
-
|
|
3485
|
+
/** Provide a shared core instance and synchronize runtime, tool, and UI configuration. */
|
|
3486
|
+
const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, messageFilter, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, enableInspector, agentId, showIntelligenceIndicator = true, useSingleEndpoint, onError, a2ui, mcpApps, defaultThrottleMs, debug, learning }) => {
|
|
3432
3487
|
const learningConfig = (0, react.useMemo)(() => toLearningConfig(learning), [learning]);
|
|
3433
3488
|
const [shouldRenderInspector, setShouldRenderInspector] = (0, react.useState)(false);
|
|
3434
3489
|
const [inspectorVisible, setInspectorVisible] = (0, react.useState)(false);
|
|
@@ -3485,11 +3540,12 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
|
|
|
3485
3540
|
const core = copilotkitRef.current;
|
|
3486
3541
|
return (0, _copilotkit_a2ui_renderer.filterCatalog)(rawCatalog, (name) => core ? core.isCatalogComponentEnabled(name) : true);
|
|
3487
3542
|
}, [rawCatalog, catalogToggleVersion]);
|
|
3543
|
+
const mcpAppsAllowSameOrigin = mcpApps?.allowSameOriginFallback === true;
|
|
3488
3544
|
const builtInActivityRenderers = (0, react.useMemo)(() => {
|
|
3489
3545
|
const renderers = [{
|
|
3490
3546
|
activityType: _copilotkit_mcp_apps_renderer_activity.MCPAppsActivityType,
|
|
3491
3547
|
content: _copilotkit_mcp_apps_renderer_activity.MCPAppsActivityContentSchema,
|
|
3492
|
-
render: MCPAppsActivityRenderer
|
|
3548
|
+
render: mcpAppsAllowSameOrigin ? SameOriginMCPAppsActivityRenderer : MCPAppsActivityRenderer
|
|
3493
3549
|
}];
|
|
3494
3550
|
if (openGenUIActive) renderers.push({
|
|
3495
3551
|
activityType: OpenGenerativeUIActivityType,
|
|
@@ -3507,7 +3563,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
|
|
|
3507
3563
|
a2uiActive,
|
|
3508
3564
|
openGenUIActive,
|
|
3509
3565
|
a2ui,
|
|
3510
|
-
filteredCatalog
|
|
3566
|
+
filteredCatalog,
|
|
3567
|
+
mcpAppsAllowSameOrigin
|
|
3511
3568
|
]);
|
|
3512
3569
|
const allActivityRenderers = (0, react.useMemo)(() => {
|
|
3513
3570
|
return [...renderActivityMessagesList, ...builtInActivityRenderers];
|
|
@@ -3615,6 +3672,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
|
|
|
3615
3672
|
description: _copilotkit_shared.GENERATE_SANDBOXED_UI_DESCRIPTION,
|
|
3616
3673
|
parameters: GenerateSandboxedUiArgsSchema,
|
|
3617
3674
|
handler: async () => "UI generated",
|
|
3675
|
+
reconnectBehavior: "resume-pending",
|
|
3618
3676
|
followUp: true,
|
|
3619
3677
|
render: OpenGenerativeUIToolRenderer
|
|
3620
3678
|
}];
|
|
@@ -4346,7 +4404,8 @@ function useRenderTool(config, deps) {
|
|
|
4346
4404
|
...props,
|
|
4347
4405
|
parameters: props.args
|
|
4348
4406
|
}),
|
|
4349
|
-
...config.agentId ? { agentId: config.agentId } : {}
|
|
4407
|
+
...config.agentId ? { agentId: config.agentId } : {},
|
|
4408
|
+
group: config.group
|
|
4350
4409
|
}) : defineToolCallRenderer({
|
|
4351
4410
|
name: config.name,
|
|
4352
4411
|
args: config.parameters,
|
|
@@ -4364,16 +4423,395 @@ function useRenderTool(config, deps) {
|
|
|
4364
4423
|
parameters: props.args
|
|
4365
4424
|
});
|
|
4366
4425
|
},
|
|
4367
|
-
...config.agentId ? { agentId: config.agentId } : {}
|
|
4426
|
+
...config.agentId ? { agentId: config.agentId } : {},
|
|
4427
|
+
group: config.group
|
|
4368
4428
|
});
|
|
4369
4429
|
copilotkit.addHookRenderToolCall(renderer);
|
|
4370
4430
|
}, [
|
|
4371
4431
|
config.name,
|
|
4432
|
+
config.group,
|
|
4372
4433
|
copilotkit,
|
|
4373
4434
|
depsVersion
|
|
4374
4435
|
]);
|
|
4375
4436
|
}
|
|
4376
4437
|
|
|
4438
|
+
//#endregion
|
|
4439
|
+
//#region src/v2/components/chat/CopilotChatToolCallGroup.tsx
|
|
4440
|
+
/**
|
|
4441
|
+
* Set by a group around the calls it shows, so CopilotKit's built-in tool card
|
|
4442
|
+
* draws itself as a compact row that fits the group instead of a card.
|
|
4443
|
+
* @internal
|
|
4444
|
+
*/
|
|
4445
|
+
const ToolCallGroupContext = (0, react.createContext)(null);
|
|
4446
|
+
/**
|
|
4447
|
+
* A call without a result is running only while the group is active: once
|
|
4448
|
+
* the run has moved on or ended, its result is never coming.
|
|
4449
|
+
*/
|
|
4450
|
+
const entryStatus = ({ toolMessage }, active) => toolMessage ? toolMessage.error ? "failed" : "done" : active ? "running" : "stopped";
|
|
4451
|
+
/** @internal */
|
|
4452
|
+
const statusLabels = {
|
|
4453
|
+
running: "Running",
|
|
4454
|
+
done: "Done",
|
|
4455
|
+
failed: "Failed",
|
|
4456
|
+
stopped: "Stopped"
|
|
4457
|
+
};
|
|
4458
|
+
/** The first non-empty string argument: usually a command, path or query. */
|
|
4459
|
+
function argumentPreview(parameters) {
|
|
4460
|
+
if (!parameters || typeof parameters !== "object") return void 0;
|
|
4461
|
+
return Object.values(parameters).find((v) => typeof v === "string" && v.trim().length > 0)?.replace(/\s+/g, " ");
|
|
4462
|
+
}
|
|
4463
|
+
const entryPreview = (entry) => argumentPreview((0, _copilotkit_shared.partialJSONParse)(entry.toolCall.function.arguments));
|
|
4464
|
+
/** "run_command ×2, read_file" */
|
|
4465
|
+
function toolNames(calls) {
|
|
4466
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4467
|
+
for (const { toolCall } of calls) {
|
|
4468
|
+
const name = toolCall.function.name;
|
|
4469
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
4470
|
+
}
|
|
4471
|
+
return [...counts].map(([name, count]) => count > 1 ? `${name} ×${count}` : name).join(", ");
|
|
4472
|
+
}
|
|
4473
|
+
const pluralize = (count, noun) => `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
4474
|
+
/**
|
|
4475
|
+
* Height transition between closed and its content's height. Closed content
|
|
4476
|
+
* stays mounted (so its state survives) but is inert.
|
|
4477
|
+
* @internal
|
|
4478
|
+
*/
|
|
4479
|
+
function Collapsible({ open, id, className, children }) {
|
|
4480
|
+
const ref = (0, react.useRef)(null);
|
|
4481
|
+
(0, react.useLayoutEffect)(() => {
|
|
4482
|
+
ref.current?.toggleAttribute("inert", !open);
|
|
4483
|
+
}, [open]);
|
|
4484
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4485
|
+
ref,
|
|
4486
|
+
id,
|
|
4487
|
+
className: (0, tailwind_merge.twMerge)("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-300 cpk:ease-[cubic-bezier(0.16,1,0.3,1)] cpk:motion-reduce:transition-none", className),
|
|
4488
|
+
style: { gridTemplateRows: open ? "1fr" : "0fr" },
|
|
4489
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4490
|
+
className: "cpk:min-h-0 cpk:overflow-hidden",
|
|
4491
|
+
children
|
|
4492
|
+
})
|
|
4493
|
+
});
|
|
4494
|
+
}
|
|
4495
|
+
function StatusIcon({ status, className }) {
|
|
4496
|
+
const Icon = {
|
|
4497
|
+
running: lucide_react.Loader2,
|
|
4498
|
+
done: lucide_react.Check,
|
|
4499
|
+
failed: lucide_react.X,
|
|
4500
|
+
stopped: lucide_react.Circle
|
|
4501
|
+
}[status];
|
|
4502
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
4503
|
+
"aria-hidden": "true",
|
|
4504
|
+
className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:text-muted-foreground", status === "running" && "cpk:animate-spin", status === "failed" && "cpk:text-destructive", className)
|
|
4505
|
+
});
|
|
4506
|
+
}
|
|
4507
|
+
function Chevron({ open, className }) {
|
|
4508
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
|
|
4509
|
+
"aria-hidden": "true",
|
|
4510
|
+
className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", open && "cpk:rotate-90", className)
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
function FailedCount({ calls }) {
|
|
4514
|
+
const failed = calls.filter((call) => call.toolMessage?.error).length;
|
|
4515
|
+
if (!failed) return null;
|
|
4516
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4517
|
+
className: "cpk:shrink-0 cpk:text-destructive",
|
|
4518
|
+
children: [failed, " failed"]
|
|
4519
|
+
});
|
|
4520
|
+
}
|
|
4521
|
+
/**
|
|
4522
|
+
* Guarded JSON.stringify for the payload blocks. A circular reference would
|
|
4523
|
+
* otherwise crash the entire React tree on render.
|
|
4524
|
+
*/
|
|
4525
|
+
function stringifyPayload(value) {
|
|
4526
|
+
if (typeof value === "string") return value;
|
|
4527
|
+
try {
|
|
4528
|
+
return JSON.stringify(value, null, 2);
|
|
4529
|
+
} catch (err) {
|
|
4530
|
+
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload; falling back to String():", err);
|
|
4531
|
+
try {
|
|
4532
|
+
return String(value);
|
|
4533
|
+
} catch {
|
|
4534
|
+
return "[unserializable]";
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
function Payload({ label, value }) {
|
|
4539
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4540
|
+
className: "cpk:text-[11px] cpk:font-medium cpk:uppercase cpk:tracking-wide cpk:text-muted-foreground",
|
|
4541
|
+
children: label
|
|
4542
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
4543
|
+
className: "cpk:m-0 cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-lg cpk:bg-muted cpk:p-2.5 cpk:font-mono cpk:text-xs cpk:leading-relaxed cpk:text-foreground cpk:whitespace-pre-wrap cpk:break-words",
|
|
4544
|
+
children: stringifyPayload(value)
|
|
4545
|
+
})] });
|
|
4546
|
+
}
|
|
4547
|
+
/**
|
|
4548
|
+
* A tool call's arguments and, once it has one, its result.
|
|
4549
|
+
* @internal
|
|
4550
|
+
*/
|
|
4551
|
+
function ToolCallPayloads({ parameters, result, className }) {
|
|
4552
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4553
|
+
className: (0, tailwind_merge.twMerge)("cpk:grid cpk:gap-3", className),
|
|
4554
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Payload, {
|
|
4555
|
+
label: "Arguments",
|
|
4556
|
+
value: parameters ?? {}
|
|
4557
|
+
}), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Payload, {
|
|
4558
|
+
label: "Result",
|
|
4559
|
+
value: result
|
|
4560
|
+
})]
|
|
4561
|
+
});
|
|
4562
|
+
}
|
|
4563
|
+
/**
|
|
4564
|
+
* The built-in tool card drawn as a row inside a group: status, name and main
|
|
4565
|
+
* argument, opening to the payloads.
|
|
4566
|
+
* @internal
|
|
4567
|
+
*/
|
|
4568
|
+
function ToolCallRow({ name, parameters, result, status, appearance, className, ...props }) {
|
|
4569
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
4570
|
+
const detailsId = (0, react.useId)();
|
|
4571
|
+
const preview = argumentPreview(parameters);
|
|
4572
|
+
const line = appearance === "line";
|
|
4573
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4574
|
+
"data-copilotkit": true,
|
|
4575
|
+
className: (0, tailwind_merge.twMerge)(!line && "cpk:border-t cpk:border-border", className),
|
|
4576
|
+
...props,
|
|
4577
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4578
|
+
type: "button",
|
|
4579
|
+
"aria-expanded": open,
|
|
4580
|
+
"aria-controls": detailsId,
|
|
4581
|
+
onClick: () => setOpen((value) => !value),
|
|
4582
|
+
className: (0, tailwind_merge.twMerge)("cpk:group/row cpk:m-0 cpk:flex cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-2 cpk:border-none cpk:bg-transparent cpk:text-left cpk:text-[13px] cpk:text-inherit cpk:transition-colors cpk:hover:bg-accent/60 cpk:focus-visible:outline-2 cpk:focus-visible:-outline-offset-2 cpk:focus-visible:outline-ring", line ? "cpk:-mx-1.5 cpk:w-[calc(100%+0.75rem)] cpk:rounded-md cpk:px-1.5 cpk:py-1" : "cpk:w-full cpk:px-3.5 cpk:py-2"),
|
|
4583
|
+
children: [
|
|
4584
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status }),
|
|
4585
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4586
|
+
"data-testid": "copilot-tool-render-name",
|
|
4587
|
+
className: (0, tailwind_merge.twMerge)("cpk:shrink-0 cpk:font-mono cpk:text-[13px] cpk:font-medium cpk:text-foreground", status === "running" && "cpk-shimmer"),
|
|
4588
|
+
children: name
|
|
4589
|
+
}),
|
|
4590
|
+
preview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4591
|
+
className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-[13px] cpk:text-muted-foreground",
|
|
4592
|
+
children: preview
|
|
4593
|
+
}),
|
|
4594
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4595
|
+
"data-testid": "copilot-tool-render-status",
|
|
4596
|
+
className: "cpk:sr-only",
|
|
4597
|
+
children: statusLabels[status]
|
|
4598
|
+
}),
|
|
4599
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {
|
|
4600
|
+
open,
|
|
4601
|
+
className: (0, tailwind_merge.twMerge)("cpk:ml-auto cpk:text-muted-foreground", !open && "cpk:opacity-0 cpk:group-hover/row:opacity-100 cpk:group-focus-visible/row:opacity-100")
|
|
4602
|
+
})
|
|
4603
|
+
]
|
|
4604
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
4605
|
+
open,
|
|
4606
|
+
id: detailsId,
|
|
4607
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallPayloads, {
|
|
4608
|
+
parameters,
|
|
4609
|
+
result,
|
|
4610
|
+
className: line ? "cpk:mt-1 cpk:mb-2 cpk:ml-5.5" : "cpk:px-3.5 cpk:pt-1 cpk:pb-3"
|
|
4611
|
+
})
|
|
4612
|
+
})]
|
|
4613
|
+
});
|
|
4614
|
+
}
|
|
4615
|
+
/**
|
|
4616
|
+
* Tool calls made in a row, collapsed into one summary. While a call runs the
|
|
4617
|
+
* summary names it ("Running read_file src/app.ts"); once they're all done it
|
|
4618
|
+
* reads "Used 3 tools" and opens to the calls.
|
|
4619
|
+
*
|
|
4620
|
+
* Each call is drawn by its registered renderer. CopilotKit's built-in card
|
|
4621
|
+
* (`useDefaultRenderTool`) draws a compact row inside the card and line
|
|
4622
|
+
* appearances; a custom wildcard renderer draws whatever it draws.
|
|
4623
|
+
*/
|
|
4624
|
+
function CopilotChatToolCallGroup({ calls, renderCall, appearance = "card", isActive = false, className, ...props }) {
|
|
4625
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
4626
|
+
const bodyId = (0, react.useId)();
|
|
4627
|
+
const latest = calls[calls.length - 1];
|
|
4628
|
+
if (!latest) return null;
|
|
4629
|
+
const current = isActive ? calls.find((call) => !call.toolMessage) ?? latest : void 0;
|
|
4630
|
+
const preview = current ? entryPreview(current) : void 0;
|
|
4631
|
+
const done = calls.filter((call) => call.toolMessage).length;
|
|
4632
|
+
const statuses = calls.map((call) => entryStatus(call, isActive));
|
|
4633
|
+
const failedIds = new Set(calls.filter((_, index) => statuses[index] === "failed").map((call) => call.toolCall.id));
|
|
4634
|
+
const status = current ? "running" : failedIds.size > 0 ? "failed" : statuses.includes("stopped") ? "stopped" : "done";
|
|
4635
|
+
const rows = (rowAppearance) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallGroupContext.Provider, {
|
|
4636
|
+
value: {
|
|
4637
|
+
appearance: rowAppearance,
|
|
4638
|
+
failedIds,
|
|
4639
|
+
active: isActive
|
|
4640
|
+
},
|
|
4641
|
+
children: calls.map((call) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(call) }, call.toolCall.id))
|
|
4642
|
+
});
|
|
4643
|
+
const groupProps = {
|
|
4644
|
+
"data-copilotkit": true,
|
|
4645
|
+
"data-testid": "copilot-tool-call-group",
|
|
4646
|
+
"data-appearance": appearance,
|
|
4647
|
+
"data-state": status,
|
|
4648
|
+
...props
|
|
4649
|
+
};
|
|
4650
|
+
const runningLabel = current && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4651
|
+
className: "cpk:shrink-0 cpk:font-medium cpk-shimmer",
|
|
4652
|
+
children: ["Running ", current.toolCall.function.name]
|
|
4653
|
+
}), preview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4654
|
+
className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:opacity-70",
|
|
4655
|
+
children: preview
|
|
4656
|
+
})] });
|
|
4657
|
+
if (appearance === "line") {
|
|
4658
|
+
if (calls.length === 1 && !current) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4659
|
+
...groupProps,
|
|
4660
|
+
className: (0, tailwind_merge.twMerge)("cpk:my-2", className),
|
|
4661
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallGroupContext.Provider, {
|
|
4662
|
+
value: {
|
|
4663
|
+
appearance: "line",
|
|
4664
|
+
failedIds,
|
|
4665
|
+
active: isActive
|
|
4666
|
+
},
|
|
4667
|
+
children: renderCall(latest)
|
|
4668
|
+
})
|
|
4669
|
+
});
|
|
4670
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4671
|
+
...groupProps,
|
|
4672
|
+
className: (0, tailwind_merge.twMerge)("cpk:my-3", className),
|
|
4673
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4674
|
+
type: "button",
|
|
4675
|
+
"aria-expanded": open,
|
|
4676
|
+
"aria-controls": bodyId,
|
|
4677
|
+
onClick: () => setOpen((value) => !value),
|
|
4678
|
+
className: "cpk:m-0 cpk:inline-flex cpk:max-w-full cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:hover:text-foreground cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring",
|
|
4679
|
+
children: [runningLabel || /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4680
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4681
|
+
className: "cpk:shrink-0 cpk:font-medium",
|
|
4682
|
+
children: ["Used ", pluralize(calls.length, "tool")]
|
|
4683
|
+
}),
|
|
4684
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4685
|
+
className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:opacity-70",
|
|
4686
|
+
children: toolNames(calls)
|
|
4687
|
+
}),
|
|
4688
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls })
|
|
4689
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, { open })]
|
|
4690
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
4691
|
+
open,
|
|
4692
|
+
id: bodyId,
|
|
4693
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4694
|
+
className: "cpk:mt-1 cpk:ml-1 cpk:border-l-2 cpk:border-border cpk:py-0.5 cpk:pl-3",
|
|
4695
|
+
children: rows("line")
|
|
4696
|
+
})
|
|
4697
|
+
})]
|
|
4698
|
+
});
|
|
4699
|
+
}
|
|
4700
|
+
if (appearance === "stack") {
|
|
4701
|
+
if (calls.length === 1) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4702
|
+
...groupProps,
|
|
4703
|
+
className: (0, tailwind_merge.twMerge)("cpk:py-1", className),
|
|
4704
|
+
children: renderCall(latest)
|
|
4705
|
+
});
|
|
4706
|
+
const behind = open ? 0 : Math.min(calls.length - 1, 2);
|
|
4707
|
+
const layer = "cpk:rounded-xl cpk:border cpk:border-border cpk:bg-card cpk:text-card-foreground";
|
|
4708
|
+
const latestStatus = entryStatus(latest, isActive);
|
|
4709
|
+
const latestPreview = entryPreview(latest);
|
|
4710
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4711
|
+
...groupProps,
|
|
4712
|
+
className: (0, tailwind_merge.twMerge)("cpk:my-3", className),
|
|
4713
|
+
style: {
|
|
4714
|
+
paddingBottom: behind * 6,
|
|
4715
|
+
...props.style
|
|
4716
|
+
},
|
|
4717
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4718
|
+
className: "cpk:relative cpk:isolate",
|
|
4719
|
+
children: [Array.from({ length: behind }, (_, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4720
|
+
"aria-hidden": "true",
|
|
4721
|
+
className: (0, tailwind_merge.twMerge)(layer, "cpk:absolute cpk:inset-0"),
|
|
4722
|
+
style: {
|
|
4723
|
+
transform: `translateY(${(index + 1) * 6}px) scaleX(${1 - (index + 1) * .045})`,
|
|
4724
|
+
zIndex: -1 - index,
|
|
4725
|
+
opacity: 1 - (index + 1) * .28
|
|
4726
|
+
}
|
|
4727
|
+
}, index)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4728
|
+
type: "button",
|
|
4729
|
+
"aria-expanded": open,
|
|
4730
|
+
"aria-controls": open ? bodyId : void 0,
|
|
4731
|
+
onClick: () => setOpen((value) => !value),
|
|
4732
|
+
className: (0, tailwind_merge.twMerge)("cpk:relative cpk:m-0 cpk:flex cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:text-left cpk:transition-colors cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring", open ? "cpk:mb-1 cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-xs cpk:text-muted-foreground cpk:hover:text-foreground" : (0, tailwind_merge.twMerge)(layer, "cpk:w-full cpk:gap-2 cpk:px-3.5 cpk:py-2.5 cpk:text-[13px] cpk:shadow-[0_1px_2px_rgb(0_0_0/0.04)] cpk:hover:bg-accent")),
|
|
4733
|
+
children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4734
|
+
pluralize(calls.length, "tool call"),
|
|
4735
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls }),
|
|
4736
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {
|
|
4737
|
+
open: true,
|
|
4738
|
+
className: "cpk:-rotate-90"
|
|
4739
|
+
}),
|
|
4740
|
+
"Collapse"
|
|
4741
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4742
|
+
className: "cpk:flex cpk:min-w-0 cpk:flex-1 cpk:items-center cpk:gap-2 cpk:animate-[cpk-stack-drop_260ms_cubic-bezier(0.16,1,0.3,1)] cpk:motion-reduce:animate-none",
|
|
4743
|
+
children: [
|
|
4744
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status: latestStatus }),
|
|
4745
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4746
|
+
className: (0, tailwind_merge.twMerge)("cpk:shrink-0 cpk:font-mono cpk:font-medium cpk:text-foreground", latestStatus === "running" && "cpk-shimmer"),
|
|
4747
|
+
children: latest.toolCall.function.name
|
|
4748
|
+
}),
|
|
4749
|
+
latestPreview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4750
|
+
className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-muted-foreground",
|
|
4751
|
+
children: latestPreview
|
|
4752
|
+
})
|
|
4753
|
+
]
|
|
4754
|
+
}, latest.toolCall.id), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4755
|
+
className: "cpk:ml-2 cpk:inline-flex cpk:shrink-0 cpk:items-center cpk:gap-2 cpk:text-xs cpk:text-muted-foreground",
|
|
4756
|
+
children: [
|
|
4757
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls }),
|
|
4758
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4759
|
+
className: "cpk:tabular-nums",
|
|
4760
|
+
children: pluralize(calls.length, "call")
|
|
4761
|
+
}),
|
|
4762
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {
|
|
4763
|
+
open: false,
|
|
4764
|
+
className: "cpk:rotate-90"
|
|
4765
|
+
})
|
|
4766
|
+
]
|
|
4767
|
+
})] })
|
|
4768
|
+
})]
|
|
4769
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4770
|
+
id: bodyId,
|
|
4771
|
+
className: "cpk:[&>*]:animate-[cpk-stack-fan_300ms_cubic-bezier(0.16,1,0.3,1)_both] cpk:motion-reduce:[&>*]:animate-none",
|
|
4772
|
+
children: calls.map((call) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(call) }, call.toolCall.id))
|
|
4773
|
+
})]
|
|
4774
|
+
});
|
|
4775
|
+
}
|
|
4776
|
+
if (calls.length === 1) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4777
|
+
...groupProps,
|
|
4778
|
+
className: (0, tailwind_merge.twMerge)("cpk:py-1", className),
|
|
4779
|
+
children: renderCall(latest)
|
|
4780
|
+
});
|
|
4781
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4782
|
+
...groupProps,
|
|
4783
|
+
className: (0, tailwind_merge.twMerge)("cpk:my-3 cpk:overflow-hidden cpk:rounded-xl cpk:border cpk:border-border cpk:bg-card cpk:text-card-foreground", className),
|
|
4784
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4785
|
+
type: "button",
|
|
4786
|
+
"aria-expanded": open,
|
|
4787
|
+
"aria-controls": bodyId,
|
|
4788
|
+
onClick: () => setOpen((value) => !value),
|
|
4789
|
+
className: "cpk:m-0 cpk:flex cpk:w-full cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:px-3.5 cpk:py-2.5 cpk:text-left cpk:text-[13px] cpk:text-inherit cpk:transition-colors cpk:hover:bg-accent/60 cpk:focus-visible:outline-2 cpk:focus-visible:-outline-offset-2 cpk:focus-visible:outline-ring",
|
|
4790
|
+
children: [
|
|
4791
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status }),
|
|
4792
|
+
runningLabel || /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4793
|
+
className: "cpk:shrink-0 cpk:font-medium cpk:text-foreground",
|
|
4794
|
+
children: ["Used ", pluralize(calls.length, "tool")]
|
|
4795
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4796
|
+
className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:text-muted-foreground",
|
|
4797
|
+
children: toolNames(calls)
|
|
4798
|
+
})] }),
|
|
4799
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4800
|
+
className: "cpk:ml-auto cpk:flex cpk:shrink-0 cpk:items-center cpk:gap-2 cpk:pl-2 cpk:text-xs cpk:text-muted-foreground",
|
|
4801
|
+
children: [status === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4802
|
+
className: "cpk:tabular-nums",
|
|
4803
|
+
children: status === "running" ? `${done} done` : statusLabels[status]
|
|
4804
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, { open })]
|
|
4805
|
+
})
|
|
4806
|
+
]
|
|
4807
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
4808
|
+
open,
|
|
4809
|
+
id: bodyId,
|
|
4810
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: rows("card") })
|
|
4811
|
+
})]
|
|
4812
|
+
});
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4377
4815
|
//#endregion
|
|
4378
4816
|
//#region src/v2/hooks/use-default-render-tool.tsx
|
|
4379
4817
|
/**
|
|
@@ -4457,7 +4895,8 @@ function useDefaultRenderTool(config, deps) {
|
|
|
4457
4895
|
const userRender = config?.render;
|
|
4458
4896
|
useRenderTool({
|
|
4459
4897
|
name: "*",
|
|
4460
|
-
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
|
|
4898
|
+
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) }),
|
|
4899
|
+
group: userRender ? config?.group === true : true
|
|
4461
4900
|
}, deps);
|
|
4462
4901
|
}
|
|
4463
4902
|
/**
|
|
@@ -4478,9 +4917,23 @@ function safeStringifyForPre(value) {
|
|
|
4478
4917
|
}
|
|
4479
4918
|
}
|
|
4480
4919
|
function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
|
|
4920
|
+
const group = (0, react.useContext)(ToolCallGroupContext);
|
|
4481
4921
|
const [isExpanded, setIsExpanded] = (0, react.useState)(false);
|
|
4482
4922
|
const isActive = status === "inProgress" || status === "executing";
|
|
4483
4923
|
const isComplete = status === "complete";
|
|
4924
|
+
if (group) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRow, {
|
|
4925
|
+
"data-testid": "copilot-tool-render",
|
|
4926
|
+
"data-tool-name": name,
|
|
4927
|
+
"data-tool-call-id": toolCallId,
|
|
4928
|
+
"data-status": status,
|
|
4929
|
+
"data-args": safeStringifyForAttr(parameters),
|
|
4930
|
+
"data-result": safeStringifyForAttr(result),
|
|
4931
|
+
name,
|
|
4932
|
+
parameters,
|
|
4933
|
+
result,
|
|
4934
|
+
status: isComplete ? group.failedIds.has(toolCallId) ? "failed" : "done" : group.active ? "running" : "stopped",
|
|
4935
|
+
appearance: group.appearance
|
|
4936
|
+
});
|
|
4484
4937
|
const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
|
|
4485
4938
|
const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
|
|
4486
4939
|
const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
|
|
@@ -6177,38 +6630,227 @@ function useLearningContainersInCurrentThread({ learningContainers }) {
|
|
|
6177
6630
|
});
|
|
6178
6631
|
}
|
|
6179
6632
|
|
|
6633
|
+
//#endregion
|
|
6634
|
+
//#region src/v2/components/chat/tool-call-display-context.ts
|
|
6635
|
+
/** @internal */
|
|
6636
|
+
const ToolCallDisplayContext = (0, react.createContext)(null);
|
|
6637
|
+
|
|
6638
|
+
//#endregion
|
|
6639
|
+
//#region src/v2/components/chat/tool-call-layout.ts
|
|
6640
|
+
function contentLength(content) {
|
|
6641
|
+
return typeof content === "string" ? content.length : JSON.stringify(content ?? "").length;
|
|
6642
|
+
}
|
|
6643
|
+
const entrySignature = ({ toolCall, toolMessage }) => [
|
|
6644
|
+
toolCall.id,
|
|
6645
|
+
toolCall.function.name,
|
|
6646
|
+
toolCall.function.arguments.length,
|
|
6647
|
+
toolMessage ? `${contentLength(toolMessage.content)}${toolMessage.error ? "!" : ""}` : "-"
|
|
6648
|
+
].join(":");
|
|
6649
|
+
function signatureOf(segments) {
|
|
6650
|
+
return segments.map((segment) => segment.kind === "call" ? `c(${entrySignature(segment.call)})` : `g${segment.trailing ? "*" : ""}(${segment.calls.map(entrySignature).join(",")})`).join("|");
|
|
6651
|
+
}
|
|
6652
|
+
/** Index of tool results by the call they answer. */
|
|
6653
|
+
function getToolResults(messages) {
|
|
6654
|
+
const results = /* @__PURE__ */ new Map();
|
|
6655
|
+
for (const message of messages) if (message.role === "tool") results.set(message.toolCallId, message);
|
|
6656
|
+
return results;
|
|
6657
|
+
}
|
|
6658
|
+
/**
|
|
6659
|
+
* Lays out the tool calls of every assistant message in `rendered`.
|
|
6660
|
+
*
|
|
6661
|
+
* Calls drawn by a renderer that opted into grouping join the group that's
|
|
6662
|
+
* open; anything else that shows between them closes it: assistant text, a
|
|
6663
|
+
* user, reasoning or activity message, or a call drawn by any other renderer. A group belongs to
|
|
6664
|
+
* the message where it starts, so later messages whose calls joined it render
|
|
6665
|
+
* no tool calls themselves.
|
|
6666
|
+
*
|
|
6667
|
+
* @param rendered - The messages as the chat renders them, in order.
|
|
6668
|
+
* @param all - Every message, for finding tool results (a transform may hide them).
|
|
6669
|
+
* @param renderers - The registered renderers.
|
|
6670
|
+
*/
|
|
6671
|
+
function getToolCallLayouts(rendered, all, renderers) {
|
|
6672
|
+
const results = getToolResults(all);
|
|
6673
|
+
const layouts = /* @__PURE__ */ new Map();
|
|
6674
|
+
let open;
|
|
6675
|
+
for (const message of rendered) {
|
|
6676
|
+
if (message.role === "tool") continue;
|
|
6677
|
+
if (message.role !== "assistant") {
|
|
6678
|
+
open = void 0;
|
|
6679
|
+
continue;
|
|
6680
|
+
}
|
|
6681
|
+
if (typeof message.content === "string" && message.content.trim()) open = void 0;
|
|
6682
|
+
const segments = [];
|
|
6683
|
+
layouts.set(message.id, segments);
|
|
6684
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
6685
|
+
for (const toolCall of message.toolCalls ?? []) toolCalls.set(toolCall.id, toolCall);
|
|
6686
|
+
for (const toolCall of toolCalls.values()) {
|
|
6687
|
+
const entry = {
|
|
6688
|
+
toolCall,
|
|
6689
|
+
toolMessage: results.get(toolCall.id)
|
|
6690
|
+
};
|
|
6691
|
+
const name = toolCall.function.name;
|
|
6692
|
+
const named = renderers.named.has(name);
|
|
6693
|
+
const rendered = named || renderers.wildcard;
|
|
6694
|
+
const groups = named ? renderers.groupedNames.has(name) : renderers.wildcardGroups;
|
|
6695
|
+
if (!rendered || !groups) {
|
|
6696
|
+
if (rendered) open = void 0;
|
|
6697
|
+
segments.push({
|
|
6698
|
+
kind: "call",
|
|
6699
|
+
id: toolCall.id,
|
|
6700
|
+
call: entry,
|
|
6701
|
+
rendered
|
|
6702
|
+
});
|
|
6703
|
+
} else if (open) open.calls.push(entry);
|
|
6704
|
+
else {
|
|
6705
|
+
open = {
|
|
6706
|
+
kind: "group",
|
|
6707
|
+
id: toolCall.id,
|
|
6708
|
+
calls: [entry],
|
|
6709
|
+
trailing: false
|
|
6710
|
+
};
|
|
6711
|
+
segments.push(open);
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
if (open) open.trailing = true;
|
|
6716
|
+
const layout = /* @__PURE__ */ new Map();
|
|
6717
|
+
for (const [id, segments] of layouts) layout.set(id, {
|
|
6718
|
+
segments,
|
|
6719
|
+
signature: signatureOf(segments)
|
|
6720
|
+
});
|
|
6721
|
+
return layout;
|
|
6722
|
+
}
|
|
6723
|
+
/**
|
|
6724
|
+
* Finds the working phase of every reply in `rendered`: its messages up to the
|
|
6725
|
+
* last one that calls a tool. Text in there was narration; what follows is the
|
|
6726
|
+
* reply itself. Replies that called no tools are omitted, except the latest,
|
|
6727
|
+
* which may be about to.
|
|
6728
|
+
*/
|
|
6729
|
+
function getTurnWork(rendered, layouts) {
|
|
6730
|
+
const turns = /* @__PURE__ */ new Map();
|
|
6731
|
+
let start = 0;
|
|
6732
|
+
while (start < rendered.length) {
|
|
6733
|
+
let end = start;
|
|
6734
|
+
while (end < rendered.length && rendered[end].role !== "user") end++;
|
|
6735
|
+
const key = start > 0 ? rendered[start - 1].id : "start";
|
|
6736
|
+
const visible = rendered.slice(start, end).filter((message) => message.role !== "tool");
|
|
6737
|
+
const isLatest = end === rendered.length;
|
|
6738
|
+
const calls = [];
|
|
6739
|
+
for (const message of visible) for (const segment of layouts.get(message.id)?.segments ?? []) if (segment.kind === "group") calls.push(...segment.calls);
|
|
6740
|
+
else if (segment.rendered) calls.push(segment.call);
|
|
6741
|
+
const shown = new Set(calls.map((entry) => entry.toolCall.id));
|
|
6742
|
+
let lastWork = -1;
|
|
6743
|
+
visible.forEach((message, index) => {
|
|
6744
|
+
if (message.role === "assistant" && message.toolCalls?.some((call) => shown.has(call.id))) lastWork = index;
|
|
6745
|
+
});
|
|
6746
|
+
const first = visible[0];
|
|
6747
|
+
if (first && (calls.length > 0 || isLatest)) {
|
|
6748
|
+
let lastAssistant = -1;
|
|
6749
|
+
visible.forEach((message, index) => {
|
|
6750
|
+
if (message.role === "assistant") lastAssistant = index;
|
|
6751
|
+
});
|
|
6752
|
+
const replyText = visible[lastAssistant]?.content;
|
|
6753
|
+
const endsWithWork = lastAssistant >= 0 && lastAssistant <= lastWork && typeof replyText === "string" && replyText.trim().length > 0;
|
|
6754
|
+
turns.set(key, {
|
|
6755
|
+
key,
|
|
6756
|
+
firstMessageId: first.id,
|
|
6757
|
+
workMessageIds: new Set(visible.slice(0, lastWork + 1).map((message) => message.id)),
|
|
6758
|
+
replyTextMessageId: endsWithWork ? visible[lastAssistant].id : void 0,
|
|
6759
|
+
calls,
|
|
6760
|
+
isLatest
|
|
6761
|
+
});
|
|
6762
|
+
}
|
|
6763
|
+
start = end + 1;
|
|
6764
|
+
}
|
|
6765
|
+
return turns;
|
|
6766
|
+
}
|
|
6767
|
+
|
|
6180
6768
|
//#endregion
|
|
6181
6769
|
//#region src/v2/components/chat/CopilotChatToolCallsView.tsx
|
|
6182
|
-
|
|
6770
|
+
const appearances = {
|
|
6771
|
+
grouped: "card",
|
|
6772
|
+
line: "line",
|
|
6773
|
+
stacked: "stack"
|
|
6774
|
+
};
|
|
6775
|
+
function CopilotChatToolCallsView({ message, messages = [], display: displayProp, group }) {
|
|
6183
6776
|
const renderToolCall = useRenderToolCall();
|
|
6777
|
+
const rendererNames = useToolCallRendererNames();
|
|
6184
6778
|
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
|
|
6185
6779
|
const subscribe = (0, react.useCallback)((onChange) => copilotkit.ɵsubscribeToLearningConfigured(onChange), [copilotkit]);
|
|
6186
6780
|
const getSnapshot = () => copilotkit.ɵlearningConfigured;
|
|
6187
6781
|
const learningConfigured = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
6782
|
+
const context = (0, react.useContext)(ToolCallDisplayContext);
|
|
6783
|
+
const display = displayProp ?? context?.display ?? "grouped";
|
|
6784
|
+
const viewLayout = context?.layouts.get(message.id);
|
|
6785
|
+
const layout = (0, react.useMemo)(() => {
|
|
6786
|
+
if (display === "separate") return void 0;
|
|
6787
|
+
return viewLayout ?? getToolCallLayouts([message], messages, rendererNames).get(message.id);
|
|
6788
|
+
}, [
|
|
6789
|
+
display,
|
|
6790
|
+
viewLayout,
|
|
6791
|
+
message,
|
|
6792
|
+
messages,
|
|
6793
|
+
rendererNames
|
|
6794
|
+
]);
|
|
6188
6795
|
if (!message.toolCalls || message.toolCalls.length === 0) return null;
|
|
6189
|
-
|
|
6796
|
+
const renderCall = (entry) => {
|
|
6190
6797
|
const rendered = renderToolCall({
|
|
6191
|
-
toolCall,
|
|
6192
|
-
toolMessage:
|
|
6798
|
+
toolCall: entry.toolCall,
|
|
6799
|
+
toolMessage: entry.toolMessage
|
|
6193
6800
|
});
|
|
6194
|
-
if (rendered === null) return
|
|
6195
|
-
if (!learningConfigured) return rendered;
|
|
6801
|
+
if (rendered === null || !learningConfigured) return rendered;
|
|
6196
6802
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6197
|
-
"data-tool-call-id": toolCall.id,
|
|
6803
|
+
"data-tool-call-id": entry.toolCall.id,
|
|
6198
6804
|
style: { display: "contents" },
|
|
6199
6805
|
children: rendered
|
|
6200
|
-
}
|
|
6806
|
+
});
|
|
6807
|
+
};
|
|
6808
|
+
if (!layout) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: message.toolCalls.map((toolCall) => {
|
|
6809
|
+
const toolMessage = messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id);
|
|
6810
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall({
|
|
6811
|
+
toolCall,
|
|
6812
|
+
toolMessage
|
|
6813
|
+
}) }, toolCall.id);
|
|
6814
|
+
}) });
|
|
6815
|
+
const workOpen = context?.workOpen.get(message.id);
|
|
6816
|
+
const groupActive = (trailing) => trailing && (context ? context.isRunning : true);
|
|
6817
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: layout.segments.map((segment) => {
|
|
6818
|
+
if (segment.kind === "call") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(segment.call) }, segment.id);
|
|
6819
|
+
if (display === "turn") {
|
|
6820
|
+
const failedIds = new Set(segment.calls.filter((call) => call.toolMessage?.error).map((call) => call.toolCall.id));
|
|
6821
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
6822
|
+
open: workOpen ?? true,
|
|
6823
|
+
className: "cpk:-mx-2",
|
|
6824
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6825
|
+
className: "cpk:px-2 cpk:py-1.5",
|
|
6826
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallGroupContext.Provider, {
|
|
6827
|
+
value: {
|
|
6828
|
+
appearance: "line",
|
|
6829
|
+
failedIds,
|
|
6830
|
+
active: groupActive(segment.trailing)
|
|
6831
|
+
},
|
|
6832
|
+
children: segment.calls.map((call) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(call) }, call.toolCall.id))
|
|
6833
|
+
})
|
|
6834
|
+
})
|
|
6835
|
+
}, segment.id);
|
|
6836
|
+
}
|
|
6837
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderSlot(group, CopilotChatToolCallGroup, {
|
|
6838
|
+
calls: segment.calls,
|
|
6839
|
+
renderCall,
|
|
6840
|
+
appearance: appearances[display] ?? "card",
|
|
6841
|
+
isActive: groupActive(segment.trailing)
|
|
6842
|
+
}) }, segment.id);
|
|
6201
6843
|
}) });
|
|
6202
6844
|
}
|
|
6203
6845
|
|
|
6204
6846
|
//#endregion
|
|
6205
6847
|
//#region src/v2/components/chat/CopilotChatInspectorButton.tsx
|
|
6206
6848
|
let shortcutsHidden = false;
|
|
6207
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
6849
|
+
const listeners$1 = /* @__PURE__ */ new Set();
|
|
6208
6850
|
const subscribe = (listener) => {
|
|
6209
|
-
listeners.add(listener);
|
|
6851
|
+
listeners$1.add(listener);
|
|
6210
6852
|
return () => {
|
|
6211
|
-
listeners.delete(listener);
|
|
6853
|
+
listeners$1.delete(listener);
|
|
6212
6854
|
};
|
|
6213
6855
|
};
|
|
6214
6856
|
const getSnapshot = () => shortcutsHidden;
|
|
@@ -6218,7 +6860,7 @@ function useInspectorShortcutsHidden() {
|
|
|
6218
6860
|
}
|
|
6219
6861
|
function hideShortcuts() {
|
|
6220
6862
|
shortcutsHidden = true;
|
|
6221
|
-
listeners.forEach((listener) => listener());
|
|
6863
|
+
listeners$1.forEach((listener) => listener());
|
|
6222
6864
|
}
|
|
6223
6865
|
function InspectorKiteIcon({ className, ...props }) {
|
|
6224
6866
|
const id = (0, react.useId)();
|
|
@@ -6484,9 +7126,321 @@ function CopilotChatInspectorButton({ title, className, onClick, onPointerDown,
|
|
|
6484
7126
|
});
|
|
6485
7127
|
}
|
|
6486
7128
|
|
|
7129
|
+
//#endregion
|
|
7130
|
+
//#region src/v2/components/ui/popover.tsx
|
|
7131
|
+
function Popover({ ...props }) {
|
|
7132
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Root, {
|
|
7133
|
+
"data-slot": "popover",
|
|
7134
|
+
...props
|
|
7135
|
+
});
|
|
7136
|
+
}
|
|
7137
|
+
function PopoverAnchor({ ...props }) {
|
|
7138
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Anchor, {
|
|
7139
|
+
"data-slot": "popover-anchor",
|
|
7140
|
+
...props
|
|
7141
|
+
});
|
|
7142
|
+
}
|
|
7143
|
+
/**
|
|
7144
|
+
* Popover content in a portal, so it floats over the page instead of taking
|
|
7145
|
+
* room in the layout, with the CopilotKit style scope on it.
|
|
7146
|
+
*/
|
|
7147
|
+
const PopoverContent = react.forwardRef(({ className, align = "start", sideOffset = 6, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Content, {
|
|
7148
|
+
ref,
|
|
7149
|
+
"data-copilotkit": true,
|
|
7150
|
+
"data-copilotkit-popover": true,
|
|
7151
|
+
"data-slot": "popover-content",
|
|
7152
|
+
align,
|
|
7153
|
+
sideOffset,
|
|
7154
|
+
className: cn("cpk:z-50 cpk:outline-none cpk:data-[state=open]:animate-in cpk:data-[state=open]:fade-in-0 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-1 cpk:data-[side=top]:slide-in-from-bottom-1", className),
|
|
7155
|
+
...props
|
|
7156
|
+
}) }));
|
|
7157
|
+
PopoverContent.displayName = "PopoverContent";
|
|
7158
|
+
|
|
7159
|
+
//#endregion
|
|
7160
|
+
//#region src/v2/lib/message-feedback.ts
|
|
7161
|
+
/** Trajectory event name for a rating of one assistant message. */
|
|
7162
|
+
const MESSAGE_FEEDBACK_EVENT_NAME = "message.feedback";
|
|
7163
|
+
/** Longest comment kept, in characters; the rest is cut so the event stays small. */
|
|
7164
|
+
const MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH = 1e3;
|
|
7165
|
+
/** Longest excerpt of the rated response sent with the rating, in characters. */
|
|
7166
|
+
const RESPONSE_EXCERPT_LENGTH = 500;
|
|
7167
|
+
const ratings = /* @__PURE__ */ new Map();
|
|
7168
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
7169
|
+
const threadKey = (agentId, threadId) => JSON.stringify([agentId, threadId]);
|
|
7170
|
+
const messageKey = (agentId, threadId, messageId) => JSON.stringify([
|
|
7171
|
+
agentId,
|
|
7172
|
+
threadId,
|
|
7173
|
+
messageId
|
|
7174
|
+
]);
|
|
7175
|
+
/** Cuts `text` to `max` characters without splitting a surrogate pair. */
|
|
7176
|
+
const clip = (text, max) => {
|
|
7177
|
+
const characters = Array.from(text);
|
|
7178
|
+
return characters.length > max ? characters.slice(0, max).join("") : text;
|
|
7179
|
+
};
|
|
7180
|
+
/** The comment as kept and sent: trimmed and capped, or none. */
|
|
7181
|
+
const normalizeComment = (comment) => {
|
|
7182
|
+
if (typeof comment !== "string") return void 0;
|
|
7183
|
+
const text = clip(comment.trim(), MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH);
|
|
7184
|
+
return text === "" ? void 0 : text;
|
|
7185
|
+
};
|
|
7186
|
+
/** The rating this page gave one message, if any. */
|
|
7187
|
+
function getMessageFeedback(agentId, threadId, messageId) {
|
|
7188
|
+
return ratings.get(messageKey(agentId, threadId, messageId));
|
|
7189
|
+
}
|
|
7190
|
+
/** Calls `listener` whenever a rating in this Thread changes. */
|
|
7191
|
+
function subscribeToMessageFeedback(agentId, threadId, listener) {
|
|
7192
|
+
const key = threadKey(agentId, threadId);
|
|
7193
|
+
let set = listeners.get(key);
|
|
7194
|
+
if (set === void 0) {
|
|
7195
|
+
set = /* @__PURE__ */ new Set();
|
|
7196
|
+
listeners.set(key, set);
|
|
7197
|
+
}
|
|
7198
|
+
set.add(listener);
|
|
7199
|
+
return () => {
|
|
7200
|
+
set.delete(listener);
|
|
7201
|
+
if (set.size === 0) listeners.delete(key);
|
|
7202
|
+
};
|
|
7203
|
+
}
|
|
7204
|
+
/**
|
|
7205
|
+
* Sets (or, with `null`, clears) this page's rating of one message and sends
|
|
7206
|
+
* it to the active Trajectory. Every change is a new event; the newest event
|
|
7207
|
+
* for a `feedbackId` is the current one. Like any Trajectory event, a change
|
|
7208
|
+
* made while no Trajectory captures, for example during a reconnect, is not
|
|
7209
|
+
* sent; the page still shows it.
|
|
7210
|
+
*/
|
|
7211
|
+
function setMessageFeedback(params) {
|
|
7212
|
+
const { copilotkit, agentId, threadId, message } = params;
|
|
7213
|
+
const comment = normalizeComment(params.feedback?.comment);
|
|
7214
|
+
const feedback = params.feedback && {
|
|
7215
|
+
rating: params.feedback.rating,
|
|
7216
|
+
...comment ? { comment } : {}
|
|
7217
|
+
};
|
|
7218
|
+
const key = messageKey(agentId, threadId, message.id);
|
|
7219
|
+
if (feedback) ratings.set(key, feedback);
|
|
7220
|
+
else ratings.delete(key);
|
|
7221
|
+
for (const listener of listeners.get(threadKey(agentId, threadId)) ?? []) listener();
|
|
7222
|
+
const runId = copilotkit.getRunIdForMessage(agentId, threadId, message.id);
|
|
7223
|
+
copilotkit.emitTrajectoryEvent(MESSAGE_FEEDBACK_EVENT_NAME, {
|
|
7224
|
+
feedbackId: `${threadId}:${message.id}`,
|
|
7225
|
+
threadId,
|
|
7226
|
+
messageId: message.id,
|
|
7227
|
+
agentId,
|
|
7228
|
+
...runId ? { runId } : {},
|
|
7229
|
+
rating: feedback?.rating ?? null,
|
|
7230
|
+
...feedback?.comment ? { comment: feedback.comment } : {},
|
|
7231
|
+
...copilotkit.ɵlearningCapturesAgentText ? { response: clip(message.content ?? "", RESPONSE_EXCERPT_LENGTH) } : {}
|
|
7232
|
+
});
|
|
7233
|
+
}
|
|
7234
|
+
|
|
7235
|
+
//#endregion
|
|
7236
|
+
//#region src/v2/components/chat/message-feedback.tsx
|
|
7237
|
+
/**
|
|
7238
|
+
* Set by the message view while Learning is on for this chat. Assistant
|
|
7239
|
+
* messages without it show thumbs only when the app passes its own handlers
|
|
7240
|
+
* or thumb slots.
|
|
7241
|
+
*/
|
|
7242
|
+
const MessageFeedbackScopeContext = (0, react.createContext)(null);
|
|
7243
|
+
const capturedCores = /* @__PURE__ */ new WeakSet();
|
|
7244
|
+
const rememberCapture = (copilotkit) => {
|
|
7245
|
+
if (capturedCores.has(copilotkit)) return;
|
|
7246
|
+
capturedCores.add(copilotkit);
|
|
7247
|
+
const unsubscribe = copilotkit.ɵsubscribeToLearningConfigured(() => {
|
|
7248
|
+
if (copilotkit.ɵlearningConfigured) return;
|
|
7249
|
+
capturedCores.delete(copilotkit);
|
|
7250
|
+
unsubscribe();
|
|
7251
|
+
});
|
|
7252
|
+
};
|
|
7253
|
+
/**
|
|
7254
|
+
* Whether the thumbs record feedback for Learning. True once a Trajectory of
|
|
7255
|
+
* this Core has started and as long as Learning stays configured, also while
|
|
7256
|
+
* capture reconnects or is stopped, so the buttons do not flicker. Ratings
|
|
7257
|
+
* given meanwhile still show but, like any Trajectory event, are not
|
|
7258
|
+
* sent. A capture that never starts (for example without a license) leaves it
|
|
7259
|
+
* false.
|
|
7260
|
+
*/
|
|
7261
|
+
function useLearningFeedbackEnabled(copilotkit) {
|
|
7262
|
+
const configured = (0, react.useSyncExternalStore)((0, react.useCallback)((onChange) => copilotkit.ɵsubscribeToLearningConfigured(onChange), [copilotkit]), () => copilotkit.ɵlearningConfigured, () => false);
|
|
7263
|
+
const capturing = (0, react.useSyncExternalStore)((0, react.useCallback)((onChange) => copilotkit.subscribe({ onTrajectoryChanged: onChange }).unsubscribe, [copilotkit]), () => copilotkit.trajectoryId !== null, () => false);
|
|
7264
|
+
(0, react.useEffect)(() => {
|
|
7265
|
+
if (capturing) rememberCapture(copilotkit);
|
|
7266
|
+
}, [capturing, copilotkit]);
|
|
7267
|
+
return configured && (capturing || capturedCores.has(copilotkit));
|
|
7268
|
+
}
|
|
7269
|
+
const noSubscription = () => () => {};
|
|
7270
|
+
/** Feedback controls for `message`, or `null` when this chat collects none. */
|
|
7271
|
+
function useMessageFeedback(message) {
|
|
7272
|
+
const scope = (0, react.useContext)(MessageFeedbackScopeContext);
|
|
7273
|
+
const subscribe = (0, react.useCallback)((onChange) => scope ? subscribeToMessageFeedback(scope.agentId, scope.threadId, onChange) : noSubscription(), [scope]);
|
|
7274
|
+
const getSnapshot = () => scope ? getMessageFeedback(scope.agentId, scope.threadId, message.id) : void 0;
|
|
7275
|
+
const feedback = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
7276
|
+
if (!scope) return null;
|
|
7277
|
+
const save = (next) => setMessageFeedback({
|
|
7278
|
+
...scope,
|
|
7279
|
+
message,
|
|
7280
|
+
feedback: next
|
|
7281
|
+
});
|
|
7282
|
+
return {
|
|
7283
|
+
feedback,
|
|
7284
|
+
rate: (rating) => {
|
|
7285
|
+
if (feedback?.rating !== rating) save({ rating });
|
|
7286
|
+
},
|
|
7287
|
+
clear: () => {
|
|
7288
|
+
if (feedback) save(null);
|
|
7289
|
+
},
|
|
7290
|
+
comment: (comment) => {
|
|
7291
|
+
if (feedback) save({
|
|
7292
|
+
rating: feedback.rating,
|
|
7293
|
+
comment
|
|
7294
|
+
});
|
|
7295
|
+
}
|
|
7296
|
+
};
|
|
7297
|
+
}
|
|
7298
|
+
/**
|
|
7299
|
+
* How long the pointer may be away from the rating and its comment before the
|
|
7300
|
+
* comment closes: enough to cross the gap between them, short enough that
|
|
7301
|
+
* passing over a rating does not leave it open.
|
|
7302
|
+
*/
|
|
7303
|
+
const FEEDBACK_COMMENT_CLOSE_DELAY_MS = 150;
|
|
7304
|
+
/**
|
|
7305
|
+
* Drives the comment popover of a rated message.
|
|
7306
|
+
*
|
|
7307
|
+
* It opens when a rating is given and whenever the pointer or keyboard focus
|
|
7308
|
+
* reaches the chosen thumb, with the saved comment in the field. It closes by
|
|
7309
|
+
* itself shortly after the pointer leaves the thumb and the popover, unless
|
|
7310
|
+
* focus is in the field: then it stays until focus moves away, Enter, or
|
|
7311
|
+
* Escape. Closing saves a changed comment; Escape, a click on a thumb, and
|
|
7312
|
+
* removing the rating discard it.
|
|
7313
|
+
*/
|
|
7314
|
+
function useFeedbackComment(controls) {
|
|
7315
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
7316
|
+
const [draft, setDraft] = (0, react.useState)("");
|
|
7317
|
+
const [focusRequest, setFocusRequest] = (0, react.useState)(0);
|
|
7318
|
+
const inputRef = (0, react.useRef)(null);
|
|
7319
|
+
const timer = (0, react.useRef)(void 0);
|
|
7320
|
+
const editing = (0, react.useRef)(false);
|
|
7321
|
+
const focusWaiting = (0, react.useRef)(false);
|
|
7322
|
+
const pointerOver = (0, react.useRef)(false);
|
|
7323
|
+
const returning = (0, react.useRef)(false);
|
|
7324
|
+
const latest = (0, react.useRef)({
|
|
7325
|
+
controls,
|
|
7326
|
+
draft,
|
|
7327
|
+
open
|
|
7328
|
+
});
|
|
7329
|
+
latest.current = {
|
|
7330
|
+
...latest.current,
|
|
7331
|
+
controls,
|
|
7332
|
+
draft,
|
|
7333
|
+
open
|
|
7334
|
+
};
|
|
7335
|
+
const takeFocusRequest = (0, react.useCallback)(() => {
|
|
7336
|
+
const waiting = focusWaiting.current;
|
|
7337
|
+
focusWaiting.current = false;
|
|
7338
|
+
return waiting;
|
|
7339
|
+
}, []);
|
|
7340
|
+
const cancelClose = (0, react.useCallback)(() => {
|
|
7341
|
+
if (timer.current === void 0) return;
|
|
7342
|
+
clearTimeout(timer.current);
|
|
7343
|
+
timer.current = void 0;
|
|
7344
|
+
}, []);
|
|
7345
|
+
(0, react.useEffect)(() => cancelClose, [cancelClose]);
|
|
7346
|
+
const savedComment = (0, react.useCallback)(() => latest.current.controls?.feedback?.comment ?? "", []);
|
|
7347
|
+
(0, react.useEffect)(() => () => {
|
|
7348
|
+
const { open: wasOpen, controls: current, draft: text } = latest.current;
|
|
7349
|
+
if (wasOpen && current?.feedback && text.trim() !== savedComment()) current.comment(text);
|
|
7350
|
+
}, []);
|
|
7351
|
+
const close = (0, react.useCallback)(({ save = true } = {}) => {
|
|
7352
|
+
cancelClose();
|
|
7353
|
+
focusWaiting.current = false;
|
|
7354
|
+
pointerOver.current = false;
|
|
7355
|
+
if (!latest.current.open) return;
|
|
7356
|
+
const current = latest.current.controls;
|
|
7357
|
+
if (save && current?.feedback && latest.current.draft.trim() !== savedComment()) current.comment(latest.current.draft);
|
|
7358
|
+
editing.current = false;
|
|
7359
|
+
latest.current = {
|
|
7360
|
+
...latest.current,
|
|
7361
|
+
open: false
|
|
7362
|
+
};
|
|
7363
|
+
setOpen(false);
|
|
7364
|
+
}, [cancelClose, savedComment]);
|
|
7365
|
+
const show = ({ comment, focus }) => {
|
|
7366
|
+
cancelClose();
|
|
7367
|
+
latest.current = {
|
|
7368
|
+
...latest.current,
|
|
7369
|
+
open: true,
|
|
7370
|
+
draft: comment
|
|
7371
|
+
};
|
|
7372
|
+
setDraft(comment);
|
|
7373
|
+
if (focus) {
|
|
7374
|
+
focusWaiting.current = true;
|
|
7375
|
+
setFocusRequest((request) => request + 1);
|
|
7376
|
+
}
|
|
7377
|
+
setOpen(true);
|
|
7378
|
+
};
|
|
7379
|
+
return {
|
|
7380
|
+
open,
|
|
7381
|
+
draft,
|
|
7382
|
+
setDraft,
|
|
7383
|
+
focusRequest,
|
|
7384
|
+
takeFocusRequest,
|
|
7385
|
+
inputRef,
|
|
7386
|
+
show,
|
|
7387
|
+
enter: (via = "focus") => {
|
|
7388
|
+
cancelClose();
|
|
7389
|
+
if (via === "pointer") pointerOver.current = true;
|
|
7390
|
+
if (returning.current) return;
|
|
7391
|
+
if (!latest.current.open && latest.current.controls?.feedback) show({
|
|
7392
|
+
comment: savedComment(),
|
|
7393
|
+
focus: false
|
|
7394
|
+
});
|
|
7395
|
+
},
|
|
7396
|
+
leave: (via = "focus") => {
|
|
7397
|
+
if (via === "pointer") pointerOver.current = false;
|
|
7398
|
+
else if (pointerOver.current) return;
|
|
7399
|
+
cancelClose();
|
|
7400
|
+
timer.current = setTimeout(() => {
|
|
7401
|
+
timer.current = void 0;
|
|
7402
|
+
if (!editing.current) close();
|
|
7403
|
+
}, FEEDBACK_COMMENT_CLOSE_DELAY_MS);
|
|
7404
|
+
},
|
|
7405
|
+
setEditing: (value) => {
|
|
7406
|
+
editing.current = value;
|
|
7407
|
+
},
|
|
7408
|
+
close,
|
|
7409
|
+
returnFocus: (target) => {
|
|
7410
|
+
returning.current = true;
|
|
7411
|
+
try {
|
|
7412
|
+
target?.focus();
|
|
7413
|
+
} finally {
|
|
7414
|
+
returning.current = false;
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
};
|
|
7418
|
+
}
|
|
7419
|
+
|
|
6487
7420
|
//#endregion
|
|
6488
7421
|
//#region src/v2/components/chat/CopilotChatAssistantMessage.tsx
|
|
6489
7422
|
/**
|
|
7423
|
+
* In the `"turn"` tool-call display, text in a reply's working phase was
|
|
7424
|
+
* narration: it fades to muted once a call follows it, and folds away with the
|
|
7425
|
+
* rest of the work when the reply is done. The wrapper is there for every
|
|
7426
|
+
* message in that display, so text that turns into narration keeps its node
|
|
7427
|
+
* (and its place) rather than remounting.
|
|
7428
|
+
*/
|
|
7429
|
+
function WorkText({ messageId, children }) {
|
|
7430
|
+
const context = (0, react.useContext)(ToolCallDisplayContext);
|
|
7431
|
+
if (context?.display !== "turn") return children;
|
|
7432
|
+
const open = context.workTextOpen.get(messageId);
|
|
7433
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
7434
|
+
open: open ?? true,
|
|
7435
|
+
className: "cpk:-mx-2",
|
|
7436
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7437
|
+
className: "cpk:px-2",
|
|
7438
|
+
"data-work-text": open === void 0 ? "reply" : "narration",
|
|
7439
|
+
children
|
|
7440
|
+
})
|
|
7441
|
+
});
|
|
7442
|
+
}
|
|
7443
|
+
/**
|
|
6490
7444
|
* Binds the Inspector action to the active chat without making the full
|
|
6491
7445
|
* assistant message subscribe to chat configuration changes.
|
|
6492
7446
|
*/
|
|
@@ -6501,31 +7455,216 @@ function BoundInspectorButton({ inspectorButton, messageId, openInspector, ...pr
|
|
|
6501
7455
|
})
|
|
6502
7456
|
});
|
|
6503
7457
|
}
|
|
6504
|
-
|
|
7458
|
+
/** Whether focus came from the keyboard; a pointer click also focuses buttons. */
|
|
7459
|
+
function isFocusVisible(element) {
|
|
7460
|
+
try {
|
|
7461
|
+
return element.matches(":focus-visible");
|
|
7462
|
+
} catch {
|
|
7463
|
+
return false;
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
const HIDDEN_TOO = {
|
|
7467
|
+
checkVisibilityCSS: true,
|
|
7468
|
+
visibilityProperty: true
|
|
7469
|
+
};
|
|
7470
|
+
const FOCUSABLE = [
|
|
7471
|
+
"a[href]",
|
|
7472
|
+
"button:not([disabled])",
|
|
7473
|
+
"input:not([disabled])",
|
|
7474
|
+
"select:not([disabled])",
|
|
7475
|
+
"textarea:not([disabled])",
|
|
7476
|
+
"[tabindex]"
|
|
7477
|
+
].map((selector) => `${selector}:not([tabindex="-1"]):not([data-radix-focus-guard])`).join(", ");
|
|
7478
|
+
/** Approximately the element Tab reaches after `element`, outside `skip`. */
|
|
7479
|
+
function nextFocusable(element, skip) {
|
|
7480
|
+
const all = Array.from(element.ownerDocument.querySelectorAll(FOCUSABLE)).filter((candidate) => candidate === element || !skip?.contains(candidate) && !candidate.closest("[hidden], [inert]"));
|
|
7481
|
+
const index = all.indexOf(element);
|
|
7482
|
+
return index === -1 ? null : all[index + 1] ?? null;
|
|
7483
|
+
}
|
|
7484
|
+
/**
|
|
7485
|
+
* The comment popover of a rated message, pointing at the chosen thumb. Kept
|
|
7486
|
+
* apart so only it, not the whole message, follows chat configuration.
|
|
7487
|
+
*
|
|
7488
|
+
* The popover lives in a portal, so it keeps keyboard order by hand: Shift+Tab
|
|
7489
|
+
* from its start returns to the thumb, Tab from its end moves on to the
|
|
7490
|
+
* control after the thumb, and Enter or Escape inside it returns to the thumb.
|
|
7491
|
+
*/
|
|
7492
|
+
function FeedbackCommentPopover({ feedbackComment, comment, savedComment, rating, anchor, messageId }) {
|
|
7493
|
+
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
7494
|
+
const contentRef = (0, react.useRef)(null);
|
|
7495
|
+
const focusInside = () => contentRef.current?.contains(document.activeElement) ?? false;
|
|
7496
|
+
const { focusRequest, takeFocusRequest, inputRef } = comment;
|
|
7497
|
+
const focusField = (0, react.useCallback)(() => {
|
|
7498
|
+
if (inputRef.current && takeFocusRequest()) inputRef.current.focus();
|
|
7499
|
+
}, [inputRef, takeFocusRequest]);
|
|
7500
|
+
(0, react.useEffect)(() => focusField(), [focusRequest, focusField]);
|
|
7501
|
+
/** Whether `target` is a thumb of this message, whose click decides itself. */
|
|
7502
|
+
const isOwnThumb = (target) => {
|
|
7503
|
+
return (target instanceof Element ? target.closest("[data-copilotkit-rating]") : null)?.closest("[data-message-id]")?.getAttribute("data-message-id") === messageId;
|
|
7504
|
+
};
|
|
7505
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Popover, {
|
|
7506
|
+
open: comment.open,
|
|
7507
|
+
onOpenChange: (open) => {
|
|
7508
|
+
if (!open) comment.close();
|
|
7509
|
+
},
|
|
7510
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverAnchor, { virtualRef: anchor }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverContent, {
|
|
7511
|
+
ref: contentRef,
|
|
7512
|
+
side: "bottom",
|
|
7513
|
+
collisionPadding: 8,
|
|
7514
|
+
hideWhenDetached: true,
|
|
7515
|
+
"aria-label": labels.assistantMessageFeedbackCommentPlaceholder,
|
|
7516
|
+
"data-copilotkit-ignore": true,
|
|
7517
|
+
onOpenAutoFocus: (event) => {
|
|
7518
|
+
event.preventDefault();
|
|
7519
|
+
focusField();
|
|
7520
|
+
},
|
|
7521
|
+
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
7522
|
+
onInteractOutside: (event) => {
|
|
7523
|
+
if (isOwnThumb(event.target)) event.preventDefault();
|
|
7524
|
+
},
|
|
7525
|
+
onEscapeKeyDown: (event) => {
|
|
7526
|
+
const returnFocus = focusInside();
|
|
7527
|
+
if (returnFocus) event.stopPropagation();
|
|
7528
|
+
comment.close({ save: false });
|
|
7529
|
+
if (returnFocus) comment.returnFocus(anchor.current);
|
|
7530
|
+
},
|
|
7531
|
+
onKeyDown: (event) => {
|
|
7532
|
+
if (event.key !== "Tab") return;
|
|
7533
|
+
const focusables = Array.from(contentRef.current?.querySelectorAll(FOCUSABLE) ?? []);
|
|
7534
|
+
if (!(event.shiftKey ? document.activeElement === focusables[0] : document.activeElement === focusables[focusables.length - 1]) || !anchor.current?.isConnected) return;
|
|
7535
|
+
event.preventDefault();
|
|
7536
|
+
const target = event.shiftKey ? anchor.current : nextFocusable(anchor.current, contentRef.current) ?? anchor.current;
|
|
7537
|
+
comment.close();
|
|
7538
|
+
if (target === anchor.current) comment.returnFocus(target);
|
|
7539
|
+
else target.focus();
|
|
7540
|
+
},
|
|
7541
|
+
onPointerEnter: () => comment.enter("pointer"),
|
|
7542
|
+
onPointerLeave: () => comment.leave("pointer"),
|
|
7543
|
+
className: "cpk:z-[1300]",
|
|
7544
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlot(feedbackComment, CopilotChatAssistantMessage.FeedbackComment, {
|
|
7545
|
+
value: comment.draft,
|
|
7546
|
+
savedComment,
|
|
7547
|
+
inputRef: comment.inputRef,
|
|
7548
|
+
onChange: comment.setDraft,
|
|
7549
|
+
onSubmit: () => {
|
|
7550
|
+
comment.close();
|
|
7551
|
+
comment.returnFocus(anchor.current);
|
|
7552
|
+
},
|
|
7553
|
+
onEditingChange: comment.setEditing,
|
|
7554
|
+
onLeave: (next) => {
|
|
7555
|
+
if (next && (contentRef.current?.contains(next) || isOwnThumb(next))) return;
|
|
7556
|
+
comment.leave();
|
|
7557
|
+
}
|
|
7558
|
+
}) }, rating)
|
|
7559
|
+
})]
|
|
7560
|
+
});
|
|
7561
|
+
}
|
|
7562
|
+
function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, feedbackComment: feedbackCommentSlot, toolCallsView, children, className, ...props }) {
|
|
6505
7563
|
useKatexStyles();
|
|
6506
7564
|
const { isInspectorEnabled, openInspector } = useCopilotKitInspector();
|
|
6507
7565
|
const shortcutsHidden = useInspectorShortcutsHidden();
|
|
6508
7566
|
const showInspectorShortcut = isInspectorEnabled && !shortcutsHidden;
|
|
7567
|
+
const feedback = useMessageFeedback(message);
|
|
7568
|
+
const comment = useFeedbackComment(feedback);
|
|
7569
|
+
const rating = feedback?.feedback?.rating;
|
|
7570
|
+
const root = (0, react.useRef)(null);
|
|
7571
|
+
const commentAnchor = (0, react.useRef)(null);
|
|
7572
|
+
const commentRating = (0, react.useRef)(rating);
|
|
7573
|
+
(0, react.useLayoutEffect)(() => {
|
|
7574
|
+
if (comment.open && !commentAnchor.current?.isConnected && rating) commentAnchor.current = root.current?.querySelector(`[data-copilotkit-rating="${rating}"]`) ?? root.current?.querySelector("button[aria-pressed=\"true\"]") ?? null;
|
|
7575
|
+
});
|
|
7576
|
+
/**
|
|
7577
|
+
* Thumbs are toggles: a new rating is saved and opens its comment, and
|
|
7578
|
+
* clicking the chosen thumb again removes the rating. A keyboard click
|
|
7579
|
+
* (`detail` 0) moves focus into the comment, as there is no pointer.
|
|
7580
|
+
* Custom thumb slots may call `onClick` without an event.
|
|
7581
|
+
*
|
|
7582
|
+
* @returns Whether the app's handler should run: true for a new rating and
|
|
7583
|
+
* whenever this chat does not record ratings, false when one is removed.
|
|
7584
|
+
*/
|
|
7585
|
+
const rate = (value, event) => {
|
|
7586
|
+
if (!feedback) return true;
|
|
7587
|
+
if (rating === value) {
|
|
7588
|
+
comment.close({ save: false });
|
|
7589
|
+
feedback.clear();
|
|
7590
|
+
return false;
|
|
7591
|
+
}
|
|
7592
|
+
feedback.rate(value);
|
|
7593
|
+
commentRating.current = value;
|
|
7594
|
+
commentAnchor.current = event?.currentTarget ?? null;
|
|
7595
|
+
comment.show({
|
|
7596
|
+
comment: "",
|
|
7597
|
+
focus: event?.detail === 0
|
|
7598
|
+
});
|
|
7599
|
+
return true;
|
|
7600
|
+
};
|
|
7601
|
+
/** Props that let the chosen thumb open and point at its comment. */
|
|
7602
|
+
const thumbFeedbackProps = (value) => {
|
|
7603
|
+
if (!feedback) return {};
|
|
7604
|
+
const thumb = { "data-copilotkit-rating": value };
|
|
7605
|
+
if (rating !== value) return {
|
|
7606
|
+
...thumb,
|
|
7607
|
+
"aria-pressed": false
|
|
7608
|
+
};
|
|
7609
|
+
const enter = (target, via) => {
|
|
7610
|
+
if (!comment.open) commentRating.current = value;
|
|
7611
|
+
commentAnchor.current = target;
|
|
7612
|
+
comment.enter(via);
|
|
7613
|
+
};
|
|
7614
|
+
return {
|
|
7615
|
+
...thumb,
|
|
7616
|
+
"aria-pressed": true,
|
|
7617
|
+
"aria-haspopup": "dialog",
|
|
7618
|
+
"aria-expanded": comment.open,
|
|
7619
|
+
onPointerEnter: (event) => {
|
|
7620
|
+
if (event.pointerType !== "touch") enter(event.currentTarget, "pointer");
|
|
7621
|
+
},
|
|
7622
|
+
onPointerLeave: (event) => {
|
|
7623
|
+
if (event.pointerType !== "touch") comment.leave("pointer");
|
|
7624
|
+
},
|
|
7625
|
+
onFocus: (event) => {
|
|
7626
|
+
if (isFocusVisible(event.currentTarget)) enter(event.currentTarget, "focus");
|
|
7627
|
+
},
|
|
7628
|
+
onBlur: () => comment.leave(),
|
|
7629
|
+
onKeyDown: (event) => {
|
|
7630
|
+
const input = comment.inputRef.current;
|
|
7631
|
+
if (event.key === "Tab" && !event.shiftKey && comment.open && input && input.checkVisibility?.(HIDDEN_TOO) !== false) {
|
|
7632
|
+
event.preventDefault();
|
|
7633
|
+
input.focus();
|
|
7634
|
+
}
|
|
7635
|
+
}
|
|
7636
|
+
};
|
|
7637
|
+
};
|
|
6509
7638
|
const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
|
|
6510
7639
|
const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
|
|
6511
7640
|
if (message.content) return await (0, _copilotkit_shared.copyToClipboard)(message.content);
|
|
6512
7641
|
return false;
|
|
6513
7642
|
} });
|
|
6514
|
-
const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, {
|
|
7643
|
+
const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, {
|
|
7644
|
+
onClick: onThumbsUp || feedback ? (event) => {
|
|
7645
|
+
if (rate("up", event)) onThumbsUp?.(message);
|
|
7646
|
+
} : void 0,
|
|
7647
|
+
...thumbFeedbackProps("up")
|
|
7648
|
+
});
|
|
6515
7649
|
const boundInspectorButton = showInspectorShortcut ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundInspectorButton, {
|
|
6516
7650
|
inspectorButton,
|
|
6517
7651
|
messageId: message.id,
|
|
6518
7652
|
openInspector
|
|
6519
7653
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
6520
|
-
const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, {
|
|
7654
|
+
const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, {
|
|
7655
|
+
onClick: onThumbsDown || feedback ? (event) => {
|
|
7656
|
+
if (rate("down", event)) onThumbsDown?.(message);
|
|
7657
|
+
} : void 0,
|
|
7658
|
+
...thumbFeedbackProps("down")
|
|
7659
|
+
});
|
|
6521
7660
|
const boundReadAloudButton = renderSlot(readAloudButton, CopilotChatAssistantMessage.ReadAloudButton, { onClick: onReadAloud ? () => onReadAloud(message) : void 0 });
|
|
6522
7661
|
const boundRegenerateButton = renderSlot(regenerateButton, CopilotChatAssistantMessage.RegenerateButton, { onClick: onRegenerate ? () => onRegenerate(message) : void 0 });
|
|
6523
7662
|
const boundToolbar = renderSlot(toolbar, CopilotChatAssistantMessage.Toolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6524
7663
|
className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-1",
|
|
6525
7664
|
children: [
|
|
6526
7665
|
boundCopyButton,
|
|
6527
|
-
(onThumbsUp || thumbsUpButton) && boundThumbsUpButton,
|
|
6528
|
-
(onThumbsDown || thumbsDownButton) && boundThumbsDownButton,
|
|
7666
|
+
(onThumbsUp || thumbsUpButton || feedback) && boundThumbsUpButton,
|
|
7667
|
+
(onThumbsDown || thumbsDownButton || feedback) && boundThumbsDownButton,
|
|
6529
7668
|
(onReadAloud || readAloudButton) && boundReadAloudButton,
|
|
6530
7669
|
(onRegenerate || regenerateButton) && boundRegenerateButton,
|
|
6531
7670
|
additionalToolbarItems,
|
|
@@ -6539,11 +7678,31 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
|
|
|
6539
7678
|
const hasContent = !!(message.content && message.content.trim().length > 0);
|
|
6540
7679
|
const isLatestAssistantMessage = message.role === "assistant" && (isLatest ?? messages?.[messages.length - 1]?.id === message.id);
|
|
6541
7680
|
const shouldShowToolbar = toolbarVisible && (hasContent || showInspectorShortcut) && !(isRunning && isLatestAssistantMessage);
|
|
6542
|
-
|
|
7681
|
+
const { open: commentOpen, close: closeComment } = comment;
|
|
7682
|
+
(0, react.useEffect)(() => {
|
|
7683
|
+
if (!commentOpen) return;
|
|
7684
|
+
if (rating !== commentRating.current) closeComment({ save: false });
|
|
7685
|
+
else if (!shouldShowToolbar) closeComment();
|
|
7686
|
+
}, [
|
|
7687
|
+
commentOpen,
|
|
7688
|
+
shouldShowToolbar,
|
|
7689
|
+
rating,
|
|
7690
|
+
closeComment
|
|
7691
|
+
]);
|
|
7692
|
+
const feedbackComment = feedback?.feedback && shouldShowToolbar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FeedbackCommentPopover, {
|
|
7693
|
+
feedbackComment: feedbackCommentSlot,
|
|
7694
|
+
comment,
|
|
7695
|
+
savedComment: feedback.feedback.comment ?? "",
|
|
7696
|
+
rating: feedback.feedback.rating,
|
|
7697
|
+
anchor: commentAnchor,
|
|
7698
|
+
messageId: message.id
|
|
7699
|
+
});
|
|
7700
|
+
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7701
|
+
ref: root,
|
|
6543
7702
|
"data-copilotkit": true,
|
|
6544
7703
|
"data-message-id": message.id,
|
|
6545
7704
|
style: { display: "contents" },
|
|
6546
|
-
children: children({
|
|
7705
|
+
children: [children({
|
|
6547
7706
|
markdownRenderer: boundMarkdownRenderer,
|
|
6548
7707
|
toolbar: boundToolbar,
|
|
6549
7708
|
toolCallsView: boundToolCallsView,
|
|
@@ -6562,21 +7721,26 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
|
|
|
6562
7721
|
onRegenerate,
|
|
6563
7722
|
additionalToolbarItems,
|
|
6564
7723
|
toolbarVisible: shouldShowToolbar
|
|
6565
|
-
})
|
|
7724
|
+
}), feedbackComment]
|
|
6566
7725
|
});
|
|
6567
7726
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7727
|
+
ref: root,
|
|
6568
7728
|
"data-copilotkit": true,
|
|
6569
7729
|
"data-testid": "copilot-assistant-message",
|
|
6570
7730
|
className: (0, tailwind_merge.twMerge)("copilotKitMessage copilotKitAssistantMessage", className),
|
|
6571
7731
|
...props,
|
|
6572
7732
|
"data-message-id": message.id,
|
|
6573
7733
|
children: [
|
|
6574
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
6575
|
-
|
|
6576
|
-
children:
|
|
7734
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkText, {
|
|
7735
|
+
messageId: message.id,
|
|
7736
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7737
|
+
className: "cpk:prose cpk:max-w-full cpk:break-words cpk:dark:prose-invert",
|
|
7738
|
+
children: boundMarkdownRenderer
|
|
7739
|
+
})
|
|
6577
7740
|
}),
|
|
6578
7741
|
boundToolCallsView,
|
|
6579
|
-
shouldShowToolbar && boundToolbar
|
|
7742
|
+
shouldShowToolbar && boundToolbar,
|
|
7743
|
+
feedbackComment
|
|
6580
7744
|
]
|
|
6581
7745
|
});
|
|
6582
7746
|
}
|
|
@@ -6592,20 +7756,27 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
|
|
|
6592
7756
|
...props
|
|
6593
7757
|
});
|
|
6594
7758
|
const ToolbarButton = _CopilotChatAssistantMessage.ToolbarButton = ({ title, tooltip, tooltipClassName, children, ...props }) => {
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
7759
|
+
const [tooltipOpen, setTooltipOpen] = (0, react.useState)(false);
|
|
7760
|
+
const popupOpen = props["aria-expanded"] === true;
|
|
7761
|
+
if (popupOpen && tooltipOpen) setTooltipOpen(false);
|
|
7762
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, {
|
|
7763
|
+
open: tooltipOpen && !popupOpen,
|
|
7764
|
+
onOpenChange: (open) => setTooltipOpen(open && !popupOpen),
|
|
7765
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
|
|
7766
|
+
asChild: true,
|
|
7767
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
7768
|
+
type: "button",
|
|
7769
|
+
variant: "assistantMessageToolbarButton",
|
|
7770
|
+
"aria-label": title,
|
|
7771
|
+
...props,
|
|
7772
|
+
children
|
|
7773
|
+
})
|
|
7774
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
|
|
7775
|
+
side: "bottom",
|
|
7776
|
+
className: tooltipClassName,
|
|
7777
|
+
children: tooltip ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: title })
|
|
7778
|
+
})]
|
|
7779
|
+
});
|
|
6609
7780
|
};
|
|
6610
7781
|
_CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
|
|
6611
7782
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
@@ -6644,7 +7815,10 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
|
|
|
6644
7815
|
"data-testid": "copilot-thumbs-up-button",
|
|
6645
7816
|
title: title || labels.assistantMessageToolbarThumbsUpLabel,
|
|
6646
7817
|
...props,
|
|
6647
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsUp, {
|
|
7818
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsUp, {
|
|
7819
|
+
className: "cpk:size-[18px]",
|
|
7820
|
+
fill: props["aria-pressed"] === true ? "currentColor" : "none"
|
|
7821
|
+
})
|
|
6648
7822
|
});
|
|
6649
7823
|
};
|
|
6650
7824
|
_CopilotChatAssistantMessage.ThumbsDownButton = ({ title, ...props }) => {
|
|
@@ -6653,7 +7827,45 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
|
|
|
6653
7827
|
"data-testid": "copilot-thumbs-down-button",
|
|
6654
7828
|
title: title || labels.assistantMessageToolbarThumbsDownLabel,
|
|
6655
7829
|
...props,
|
|
6656
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsDown, {
|
|
7830
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsDown, {
|
|
7831
|
+
className: "cpk:size-[18px]",
|
|
7832
|
+
fill: props["aria-pressed"] === true ? "currentColor" : "none"
|
|
7833
|
+
})
|
|
7834
|
+
});
|
|
7835
|
+
};
|
|
7836
|
+
_CopilotChatAssistantMessage.FeedbackComment = ({ value, savedComment, inputRef, onChange, onSubmit, onEditingChange, onLeave, className }) => {
|
|
7837
|
+
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
7838
|
+
const changed = value.trim() !== savedComment;
|
|
7839
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
7840
|
+
"data-testid": "copilot-feedback-comment",
|
|
7841
|
+
className: (0, tailwind_merge.twMerge)("cpk:flex cpk:w-80 cpk:max-w-[calc(100vw-1rem)] cpk:items-center cpk:gap-1 cpk:rounded-full cpk:border cpk:border-border cpk:bg-background cpk:py-0.5 cpk:pl-3 cpk:pr-0.5 cpk:text-sm cpk:shadow-md", className),
|
|
7842
|
+
onSubmit: (event) => {
|
|
7843
|
+
event.preventDefault();
|
|
7844
|
+
onSubmit();
|
|
7845
|
+
},
|
|
7846
|
+
onFocus: () => onEditingChange?.(true),
|
|
7847
|
+
onBlur: (event) => {
|
|
7848
|
+
const next = event.relatedTarget instanceof Element ? event.relatedTarget : null;
|
|
7849
|
+
if (event.currentTarget.contains(next)) return;
|
|
7850
|
+
if (typeof document !== "undefined" && !document.hasFocus()) return;
|
|
7851
|
+
onEditingChange?.(false);
|
|
7852
|
+
onLeave?.(next);
|
|
7853
|
+
},
|
|
7854
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7855
|
+
ref: inputRef,
|
|
7856
|
+
value,
|
|
7857
|
+
maxLength: MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH,
|
|
7858
|
+
onChange: (event) => onChange(event.target.value),
|
|
7859
|
+
placeholder: labels.assistantMessageFeedbackCommentPlaceholder,
|
|
7860
|
+
"aria-label": labels.assistantMessageFeedbackCommentPlaceholder,
|
|
7861
|
+
className: "cpk:min-w-0 cpk:flex-1 cpk:bg-transparent cpk:py-1 cpk:text-foreground cpk:outline-none cpk:placeholder:text-muted-foreground"
|
|
7862
|
+
}), changed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
7863
|
+
type: "submit",
|
|
7864
|
+
variant: "assistantMessageToolbarButton",
|
|
7865
|
+
"aria-label": labels.assistantMessageFeedbackCommentSubmitLabel,
|
|
7866
|
+
title: labels.assistantMessageFeedbackCommentSubmitLabel,
|
|
7867
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[16px]" })
|
|
7868
|
+
})]
|
|
6657
7869
|
});
|
|
6658
7870
|
};
|
|
6659
7871
|
_CopilotChatAssistantMessage.ReadAloudButton = ({ title, ...props }) => {
|
|
@@ -6681,6 +7893,7 @@ CopilotChatAssistantMessage.CopyButton.displayName = "CopilotChatAssistantMessag
|
|
|
6681
7893
|
CopilotChatAssistantMessage.InspectorButton.displayName = "CopilotChatAssistantMessage.InspectorButton";
|
|
6682
7894
|
CopilotChatAssistantMessage.ThumbsUpButton.displayName = "CopilotChatAssistantMessage.ThumbsUpButton";
|
|
6683
7895
|
CopilotChatAssistantMessage.ThumbsDownButton.displayName = "CopilotChatAssistantMessage.ThumbsDownButton";
|
|
7896
|
+
CopilotChatAssistantMessage.FeedbackComment.displayName = "CopilotChatAssistantMessage.FeedbackComment";
|
|
6684
7897
|
CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantMessage.ReadAloudButton";
|
|
6685
7898
|
CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
|
|
6686
7899
|
var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
|
|
@@ -6986,6 +8199,7 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
6986
8199
|
});
|
|
6987
8200
|
};
|
|
6988
8201
|
_CopilotChatUserMessage.BranchNavigation = ({ className, currentBranch = 0, numberOfBranches = 1, onSwitchToBranch, message, ...props }) => {
|
|
8202
|
+
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
6989
8203
|
if (!numberOfBranches || numberOfBranches <= 1 || !onSwitchToBranch) return null;
|
|
6990
8204
|
const canGoPrev = currentBranch > 0;
|
|
6991
8205
|
const canGoNext = currentBranch < numberOfBranches - 1;
|
|
@@ -7004,6 +8218,7 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
7004
8218
|
}),
|
|
7005
8219
|
disabled: !canGoPrev,
|
|
7006
8220
|
className: "cpk:h-6 cpk:w-6 cpk:p-0",
|
|
8221
|
+
"aria-label": labels.userMessageToolbarPreviousBranchLabel,
|
|
7007
8222
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeft, { className: "cpk:size-[20px]" })
|
|
7008
8223
|
}),
|
|
7009
8224
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -7024,6 +8239,7 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
7024
8239
|
}),
|
|
7025
8240
|
disabled: !canGoNext,
|
|
7026
8241
|
className: "cpk:h-6 cpk:w-6 cpk:p-0",
|
|
8242
|
+
"aria-label": labels.userMessageToolbarNextBranchLabel,
|
|
7027
8243
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: "cpk:size-[20px]" })
|
|
7028
8244
|
})
|
|
7029
8245
|
]
|
|
@@ -7044,7 +8260,7 @@ var CopilotChatUserMessage_default = CopilotChatUserMessage;
|
|
|
7044
8260
|
/**
|
|
7045
8261
|
* Formats an elapsed duration (in seconds) to a human-readable string.
|
|
7046
8262
|
*/
|
|
7047
|
-
function formatDuration(seconds) {
|
|
8263
|
+
function formatDuration$1(seconds) {
|
|
7048
8264
|
if (seconds < 1) return "a few seconds";
|
|
7049
8265
|
if (seconds < 60) return `${Math.round(seconds)} seconds`;
|
|
7050
8266
|
const mins = Math.floor(seconds / 60);
|
|
@@ -7082,7 +8298,7 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, isLatest: i
|
|
|
7082
8298
|
userToggledRef.current = true;
|
|
7083
8299
|
setIsOpen((prev) => !prev);
|
|
7084
8300
|
} : void 0;
|
|
7085
|
-
const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
|
|
8301
|
+
const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration$1(elapsed)}`;
|
|
7086
8302
|
const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
|
|
7087
8303
|
isOpen,
|
|
7088
8304
|
label,
|
|
@@ -7304,6 +8520,71 @@ const ScrollElementContext = react.default.createContext(null);
|
|
|
7304
8520
|
*/
|
|
7305
8521
|
const ScrollPinnedContext = react.default.createContext(false);
|
|
7306
8522
|
|
|
8523
|
+
//#endregion
|
|
8524
|
+
//#region src/v2/components/chat/CopilotChatWorkSummary.tsx
|
|
8525
|
+
/** "45s", "1m 5s", "2m". Used for the live timer and the final duration. */
|
|
8526
|
+
function formatDuration(ms) {
|
|
8527
|
+
const seconds = Math.max(0, Math.floor(ms / 1e3));
|
|
8528
|
+
if (seconds < 60) return `${seconds}s`;
|
|
8529
|
+
const minutes = Math.floor(seconds / 60);
|
|
8530
|
+
const rest = seconds % 60;
|
|
8531
|
+
return rest ? `${minutes}m ${rest}s` : `${minutes}m`;
|
|
8532
|
+
}
|
|
8533
|
+
/** Re-renders every second while `active`, returning the current time. */
|
|
8534
|
+
function useNow(active) {
|
|
8535
|
+
const [now, setNow] = (0, react.useState)(() => Date.now());
|
|
8536
|
+
(0, react.useEffect)(() => {
|
|
8537
|
+
if (!active) return;
|
|
8538
|
+
setNow(Date.now());
|
|
8539
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
8540
|
+
return () => clearInterval(timer);
|
|
8541
|
+
}, [active]);
|
|
8542
|
+
return now;
|
|
8543
|
+
}
|
|
8544
|
+
/**
|
|
8545
|
+
* The line at the top of a reply in the `"turn"` tool-call display. While the
|
|
8546
|
+
* agent works it reads "Working 4s"; once the reply is done it reads "Worked
|
|
8547
|
+
* for 9s · 7 tools" and toggles the work it folded away.
|
|
8548
|
+
*/
|
|
8549
|
+
function CopilotChatWorkSummary({ isWorking, isOpen, calls, elapsedMs, workingSince, className, ...props }) {
|
|
8550
|
+
const now = useNow(isWorking);
|
|
8551
|
+
const failed = calls.filter((call) => call.toolMessage?.error).length;
|
|
8552
|
+
const liveMs = elapsedMs !== void 0 && workingSince !== void 0 ? elapsedMs + Math.max(0, now - workingSince) : void 0;
|
|
8553
|
+
const duration = elapsedMs !== void 0 ? formatDuration(Math.max(1e3, elapsedMs)) : void 0;
|
|
8554
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
8555
|
+
type: "button",
|
|
8556
|
+
"data-copilotkit": true,
|
|
8557
|
+
"data-testid": "copilot-work-summary",
|
|
8558
|
+
"data-state": isWorking ? "working" : "done",
|
|
8559
|
+
"aria-expanded": isOpen,
|
|
8560
|
+
className: (0, tailwind_merge.twMerge)("cpk:m-0 cpk:inline-flex cpk:max-w-full cpk:cursor-pointer cpk:items-center cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:hover:text-foreground cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring", className),
|
|
8561
|
+
...props,
|
|
8562
|
+
children: [isWorking ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8563
|
+
className: "cpk:font-medium cpk-shimmer",
|
|
8564
|
+
children: "Working"
|
|
8565
|
+
}), liveMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8566
|
+
className: "cpk:text-xs cpk:tabular-nums cpk:opacity-70",
|
|
8567
|
+
children: formatDuration(liveMs)
|
|
8568
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
8569
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8570
|
+
className: "cpk:font-medium",
|
|
8571
|
+
children: duration ? `Worked for ${duration}` : `Used ${pluralize(calls.length, "tool")}`
|
|
8572
|
+
}),
|
|
8573
|
+
duration && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8574
|
+
className: "cpk:text-xs cpk:opacity-70",
|
|
8575
|
+
children: pluralize(calls.length, "tool")
|
|
8576
|
+
}),
|
|
8577
|
+
failed > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
8578
|
+
className: "cpk:text-destructive",
|
|
8579
|
+
children: [failed, " failed"]
|
|
8580
|
+
})
|
|
8581
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
|
|
8582
|
+
"aria-hidden": "true",
|
|
8583
|
+
className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90")
|
|
8584
|
+
})]
|
|
8585
|
+
});
|
|
8586
|
+
}
|
|
8587
|
+
|
|
7307
8588
|
//#endregion
|
|
7308
8589
|
//#region src/v2/components/intelligence-indicator/IntelligenceIndicatorView.tsx
|
|
7309
8590
|
/**
|
|
@@ -7790,7 +9071,7 @@ function messageGroup(group) {
|
|
|
7790
9071
|
}
|
|
7791
9072
|
const GROUP_ROW_KEY_PREFIX = "copilotkit-group:";
|
|
7792
9073
|
const VIRTUALIZE_THRESHOLD = 50;
|
|
7793
|
-
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, transformMessages, groupMessages, children, className, ...props }) {
|
|
9074
|
+
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, workSummary, isRunning = false, transformMessages, groupMessages, messageFeedback = true, toolCallDisplay = "grouped", children, className, ...props }) {
|
|
7794
9075
|
const isPinnedToBottom = (0, react.useContext)(ScrollPinnedContext);
|
|
7795
9076
|
const renderCustomMessage = useRenderCustomMessages();
|
|
7796
9077
|
const { renderActivityMessage } = useRenderActivityMessage();
|
|
@@ -7831,6 +9112,100 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7831
9112
|
transformedMessages
|
|
7832
9113
|
]);
|
|
7833
9114
|
const latestRenderedId = renderedMessages[renderedMessages.length - 1]?.id;
|
|
9115
|
+
const rendererNames = useToolCallRendererNames();
|
|
9116
|
+
const toolCallLayouts = (0, react.useMemo)(() => toolCallDisplay === "separate" ? /* @__PURE__ */ new Map() : getToolCallLayouts(renderedMessages, messages, rendererNames), [
|
|
9117
|
+
toolCallDisplay,
|
|
9118
|
+
renderedMessages,
|
|
9119
|
+
messages,
|
|
9120
|
+
rendererNames
|
|
9121
|
+
]);
|
|
9122
|
+
const turnWork = (0, react.useMemo)(() => toolCallDisplay === "turn" ? getTurnWork(renderedMessages, toolCallLayouts) : void 0, [
|
|
9123
|
+
toolCallDisplay,
|
|
9124
|
+
renderedMessages,
|
|
9125
|
+
toolCallLayouts
|
|
9126
|
+
]);
|
|
9127
|
+
const turnByFirstMessage = (0, react.useMemo)(() => {
|
|
9128
|
+
const byFirst = /* @__PURE__ */ new Map();
|
|
9129
|
+
for (const turn of turnWork?.values() ?? []) byFirst.set(turn.firstMessageId, turn);
|
|
9130
|
+
return byFirst;
|
|
9131
|
+
}, [turnWork]);
|
|
9132
|
+
let latestTurnKey = "start";
|
|
9133
|
+
for (let i = renderedMessages.length - 1; i >= 0; i--) if (renderedMessages[i].role === "user") {
|
|
9134
|
+
latestTurnKey = renderedMessages[i].id;
|
|
9135
|
+
break;
|
|
9136
|
+
}
|
|
9137
|
+
const isWorkingOn = (turnKey) => isRunning && turnKey === latestTurnKey;
|
|
9138
|
+
const [toggledTurns, setToggledTurns] = (0, react.useState)(() => /* @__PURE__ */ new Map());
|
|
9139
|
+
const isWorkOpen = (turnKey) => toggledTurns.get(turnKey) ?? isWorkingOn(turnKey);
|
|
9140
|
+
const workTimesRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
9141
|
+
if (toolCallDisplay === "turn") {
|
|
9142
|
+
const now = Date.now();
|
|
9143
|
+
const workTimes = workTimesRef.current;
|
|
9144
|
+
if (isRunning) {
|
|
9145
|
+
const times = workTimes.get(latestTurnKey);
|
|
9146
|
+
if (times) times.workingSince ??= now;
|
|
9147
|
+
else if (renderedMessages[renderedMessages.length - 1]?.role === "user") workTimes.set(latestTurnKey, {
|
|
9148
|
+
elapsedMs: 0,
|
|
9149
|
+
workingSince: now
|
|
9150
|
+
});
|
|
9151
|
+
} else for (const times of workTimes.values()) {
|
|
9152
|
+
if (times.workingSince === void 0) continue;
|
|
9153
|
+
times.elapsedMs += now - times.workingSince;
|
|
9154
|
+
times.workingSince = void 0;
|
|
9155
|
+
}
|
|
9156
|
+
}
|
|
9157
|
+
const { workOpen, workTextOpen } = (0, react.useMemo)(() => {
|
|
9158
|
+
const open = /* @__PURE__ */ new Map();
|
|
9159
|
+
const textOpen = /* @__PURE__ */ new Map();
|
|
9160
|
+
for (const turn of turnWork?.values() ?? []) {
|
|
9161
|
+
const turnOpen = toggledTurns.get(turn.key) ?? (isRunning && turn.key === latestTurnKey);
|
|
9162
|
+
for (const id of turn.workMessageIds) {
|
|
9163
|
+
open.set(id, turnOpen);
|
|
9164
|
+
if (id !== turn.replyTextMessageId) textOpen.set(id, turnOpen);
|
|
9165
|
+
}
|
|
9166
|
+
}
|
|
9167
|
+
return {
|
|
9168
|
+
workOpen: open,
|
|
9169
|
+
workTextOpen: textOpen
|
|
9170
|
+
};
|
|
9171
|
+
}, [
|
|
9172
|
+
turnWork,
|
|
9173
|
+
toggledTurns,
|
|
9174
|
+
isRunning,
|
|
9175
|
+
latestTurnKey
|
|
9176
|
+
]);
|
|
9177
|
+
const toolCallDisplayContext = (0, react.useMemo)(() => ({
|
|
9178
|
+
display: toolCallDisplay,
|
|
9179
|
+
isRunning,
|
|
9180
|
+
layouts: toolCallLayouts,
|
|
9181
|
+
workOpen,
|
|
9182
|
+
workTextOpen
|
|
9183
|
+
}), [
|
|
9184
|
+
toolCallDisplay,
|
|
9185
|
+
isRunning,
|
|
9186
|
+
toolCallLayouts,
|
|
9187
|
+
workOpen,
|
|
9188
|
+
workTextOpen
|
|
9189
|
+
]);
|
|
9190
|
+
const renderWorkSummary = (turnKey, calls) => {
|
|
9191
|
+
const working = isWorkingOn(turnKey);
|
|
9192
|
+
const open = isWorkOpen(turnKey);
|
|
9193
|
+
const times = workTimesRef.current.get(turnKey);
|
|
9194
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
9195
|
+
open: working || calls.length > 0,
|
|
9196
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9197
|
+
className: "cpk:pt-1 cpk:pb-2",
|
|
9198
|
+
children: renderSlot(workSummary, CopilotChatWorkSummary, {
|
|
9199
|
+
isWorking: working,
|
|
9200
|
+
isOpen: open,
|
|
9201
|
+
calls,
|
|
9202
|
+
elapsedMs: times?.elapsedMs,
|
|
9203
|
+
workingSince: times?.workingSince,
|
|
9204
|
+
onClick: () => setToggledTurns((toggled) => new Map(toggled).set(turnKey, !open))
|
|
9205
|
+
})
|
|
9206
|
+
})
|
|
9207
|
+
}, `work-summary-${turnKey}`);
|
|
9208
|
+
};
|
|
7834
9209
|
const transformDuplicateId = (0, react.useMemo)(() => {
|
|
7835
9210
|
if (process.env.NODE_ENV === "production" || !transformMessages && !groupMessages) return;
|
|
7836
9211
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7868,8 +9243,6 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7868
9243
|
(0, _copilotkit_shared.commitRowKeyStore)(rowKeyStore, renderedMessages);
|
|
7869
9244
|
}, [rowKeyStore, renderedMessages]);
|
|
7870
9245
|
const stableGroupKey = (key) => rowRenderKeys.get(key) ?? key;
|
|
7871
|
-
const rowKey = (row) => row.type === "group" ? `${GROUP_ROW_KEY_PREFIX}${stableGroupKey(row.key)}` : rowRenderKeys.get(row.message.id) ?? row.message.id;
|
|
7872
|
-
const rowKeys = (0, react.useMemo)(() => rows.map(rowKey), [rows, rowRenderKeys]);
|
|
7873
9246
|
const groupStateRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
7874
9247
|
const groupSettersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
7875
9248
|
const groupStateThreadRef = (0, react.useRef)(config?.threadId);
|
|
@@ -7898,6 +9271,18 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7898
9271
|
const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
|
|
7899
9272
|
const agentId = config?.agentId;
|
|
7900
9273
|
const threadId = config?.threadId;
|
|
9274
|
+
const learningFeedbackEnabled = useLearningFeedbackEnabled(copilotkit);
|
|
9275
|
+
const feedbackScope = (0, react.useMemo)(() => messageFeedback && learningFeedbackEnabled && agentId !== void 0 && threadId !== void 0 ? {
|
|
9276
|
+
copilotkit,
|
|
9277
|
+
agentId,
|
|
9278
|
+
threadId
|
|
9279
|
+
} : null, [
|
|
9280
|
+
messageFeedback,
|
|
9281
|
+
learningFeedbackEnabled,
|
|
9282
|
+
copilotkit,
|
|
9283
|
+
agentId,
|
|
9284
|
+
threadId
|
|
9285
|
+
]);
|
|
7901
9286
|
const assistantSlotPropsWithFeedback = (0, react.useMemo)(() => {
|
|
7902
9287
|
const onThumbsUp = assistantSlotProps?.onThumbsUp;
|
|
7903
9288
|
const onThumbsDown = assistantSlotProps?.onThumbsDown;
|
|
@@ -7943,8 +9328,6 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7943
9328
|
total: 0,
|
|
7944
9329
|
sizes: /* @__PURE__ */ new Map()
|
|
7945
9330
|
});
|
|
7946
|
-
const rowKeysRef = react.default.useRef(rowKeys);
|
|
7947
|
-
rowKeysRef.current = rowKeys;
|
|
7948
9331
|
const firstMessageId = deduplicatedMessages[0]?.id;
|
|
7949
9332
|
const measuredThreadRef = react.default.useRef(firstMessageId);
|
|
7950
9333
|
if (measuredThreadRef.current !== firstMessageId) {
|
|
@@ -7954,16 +9337,6 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7954
9337
|
sizes: /* @__PURE__ */ new Map()
|
|
7955
9338
|
};
|
|
7956
9339
|
}
|
|
7957
|
-
const measuredKeysRef = react.default.useRef(rowKeys);
|
|
7958
|
-
if (measuredKeysRef.current !== rowKeys) {
|
|
7959
|
-
measuredKeysRef.current = rowKeys;
|
|
7960
|
-
const live = new Set(rowKeys);
|
|
7961
|
-
const measured = measuredRef.current;
|
|
7962
|
-
for (const [key, height] of measured.sizes) if (!live.has(key)) {
|
|
7963
|
-
measured.sizes.delete(key);
|
|
7964
|
-
measured.total -= height;
|
|
7965
|
-
}
|
|
7966
|
-
}
|
|
7967
9340
|
const estimateRowSize = react.default.useCallback(() => {
|
|
7968
9341
|
const { total, sizes } = measuredRef.current;
|
|
7969
9342
|
return sizes.size > 0 ? Math.max(1, Math.round(total / sizes.size)) : 100;
|
|
@@ -7971,15 +9344,13 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7971
9344
|
const measureRowElement = react.default.useCallback((el) => {
|
|
7972
9345
|
const height = el?.getBoundingClientRect().height ?? 0;
|
|
7973
9346
|
const index = Number(el?.dataset?.index);
|
|
7974
|
-
|
|
7975
|
-
if (height > 0 && key !== void 0) {
|
|
9347
|
+
if (height > 0 && Number.isInteger(index)) {
|
|
7976
9348
|
const { total, sizes } = measuredRef.current;
|
|
7977
|
-
measuredRef.current.total = total - (sizes.get(
|
|
7978
|
-
sizes.set(
|
|
9349
|
+
measuredRef.current.total = total - (sizes.get(index) ?? 0) + height;
|
|
9350
|
+
sizes.set(index, height);
|
|
7979
9351
|
}
|
|
7980
|
-
return height;
|
|
9352
|
+
return height > 0 ? height : measuredRef.current.sizes.get(index) ?? 100;
|
|
7981
9353
|
}, []);
|
|
7982
|
-
const getRowKey = react.default.useCallback((index) => rowKeys[index] ?? index, [rowKeys]);
|
|
7983
9354
|
const isPinnedToBottomRef = react.default.useRef(isPinnedToBottom);
|
|
7984
9355
|
const shouldAdjustScrollOnResize = react.default.useCallback((item, _delta, instance) => {
|
|
7985
9356
|
if (isPinnedToBottomRef.current) return false;
|
|
@@ -7988,9 +9359,8 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
7988
9359
|
}, []);
|
|
7989
9360
|
const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
|
|
7990
9361
|
count: shouldVirtualize ? rows.length : 0,
|
|
7991
|
-
getScrollElement: () => scrollElement,
|
|
9362
|
+
getScrollElement: () => shouldVirtualize ? scrollElement : null,
|
|
7992
9363
|
estimateSize: estimateRowSize,
|
|
7993
|
-
getItemKey: getRowKey,
|
|
7994
9364
|
overscan: 5,
|
|
7995
9365
|
measureElement: measureRowElement,
|
|
7996
9366
|
initialRect: {
|
|
@@ -8000,8 +9370,11 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
8000
9370
|
});
|
|
8001
9371
|
isPinnedToBottomRef.current = isPinnedToBottom;
|
|
8002
9372
|
virtualizer.shouldAdjustScrollPositionOnItemSizeChange = shouldAdjustScrollOnResize;
|
|
9373
|
+
const lastEndAlignedFirstMessageRef = react.default.useRef(void 0);
|
|
8003
9374
|
(0, react.useLayoutEffect)(() => {
|
|
8004
9375
|
if (!shouldVirtualize || !rows.length) return;
|
|
9376
|
+
if (lastEndAlignedFirstMessageRef.current === firstMessageId) return;
|
|
9377
|
+
lastEndAlignedFirstMessageRef.current = firstMessageId;
|
|
8005
9378
|
virtualizer.scrollToIndex(rows.length - 1, { align: "end" });
|
|
8006
9379
|
}, [shouldVirtualize, firstMessageId]);
|
|
8007
9380
|
const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages, renderedMessages), [deduplicatedMessages, renderedMessages]);
|
|
@@ -8009,6 +9382,8 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
8009
9382
|
const elements = [];
|
|
8010
9383
|
const stateSnapshot = renderCustomMessage ? getStateSnapshotForMessage(message.id) : void 0;
|
|
8011
9384
|
const rowKey = rowRenderKeys.get(message.id) ?? message.id;
|
|
9385
|
+
const turn = turnByFirstMessage.get(message.id);
|
|
9386
|
+
if (turn) elements.push(renderWorkSummary(turn.key, turn.calls));
|
|
8012
9387
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
8013
9388
|
message,
|
|
8014
9389
|
position: "before",
|
|
@@ -8032,14 +9407,20 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
8032
9407
|
message,
|
|
8033
9408
|
renderActivityMessage
|
|
8034
9409
|
}, rowKey));
|
|
8035
|
-
else if (message.role === "reasoning")
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
9410
|
+
else if (message.role === "reasoning") {
|
|
9411
|
+
const reasoning = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
|
|
9412
|
+
message,
|
|
9413
|
+
messages,
|
|
9414
|
+
isRunning,
|
|
9415
|
+
isLatest: message.id === latestRenderedId,
|
|
9416
|
+
ReasoningMessageComponent: ReasoningComponent,
|
|
9417
|
+
slotProps: reasoningSlotProps
|
|
9418
|
+
}, rowKey);
|
|
9419
|
+
elements.push(toolCallDisplay === "turn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
|
|
9420
|
+
open: workOpen.get(message.id) ?? true,
|
|
9421
|
+
children: reasoning
|
|
9422
|
+
}, rowKey) : reasoning);
|
|
9423
|
+
}
|
|
8043
9424
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
8044
9425
|
message,
|
|
8045
9426
|
position: "after",
|
|
@@ -8054,6 +9435,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
8054
9435
|
}, `intelligence-${intelligenceTurnId}`));
|
|
8055
9436
|
return elements.filter(Boolean);
|
|
8056
9437
|
};
|
|
9438
|
+
const rowKey = (row) => row.type === "group" ? `${GROUP_ROW_KEY_PREFIX}${stableGroupKey(row.key)}` : rowRenderKeys.get(row.message.id) ?? row.message.id;
|
|
8057
9439
|
const renderRow = (row) => {
|
|
8058
9440
|
if (row.type === "message") return renderMessageBlock(row.message);
|
|
8059
9441
|
const Wrapper = row.wrapper;
|
|
@@ -8066,52 +9448,157 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
8066
9448
|
children: row.messages.flatMap(renderMessageBlock)
|
|
8067
9449
|
}, rowKey(row))];
|
|
8068
9450
|
};
|
|
9451
|
+
const rowKeysRef = (0, react.useRef)([]);
|
|
9452
|
+
rowKeysRef.current = rows.map(rowKey);
|
|
9453
|
+
const virtualModeRef = (0, react.useRef)(shouldVirtualize);
|
|
9454
|
+
virtualModeRef.current = shouldVirtualize;
|
|
9455
|
+
(0, react.useEffect)(() => {
|
|
9456
|
+
const element = scrollElementFromCtx;
|
|
9457
|
+
if (!element) return;
|
|
9458
|
+
let anchor = null;
|
|
9459
|
+
let suspended = false;
|
|
9460
|
+
let frame = 0;
|
|
9461
|
+
const visibleRows = () => Array.from(element.querySelectorAll("[data-scroll-anchor]"));
|
|
9462
|
+
const remember = () => {
|
|
9463
|
+
if (suspended || element.clientHeight === 0 || !virtualModeRef.current) return;
|
|
9464
|
+
if (isPinnedToBottomRef.current) {
|
|
9465
|
+
anchor = null;
|
|
9466
|
+
return;
|
|
9467
|
+
}
|
|
9468
|
+
const viewport = element.getBoundingClientRect();
|
|
9469
|
+
const row = visibleRows().find((row) => {
|
|
9470
|
+
const bounds = row.getBoundingClientRect();
|
|
9471
|
+
return bounds.bottom > viewport.top && bounds.top < viewport.bottom;
|
|
9472
|
+
});
|
|
9473
|
+
if (row) anchor = {
|
|
9474
|
+
key: row.dataset.scrollAnchor,
|
|
9475
|
+
offset: row.getBoundingClientRect().top - viewport.top
|
|
9476
|
+
};
|
|
9477
|
+
};
|
|
9478
|
+
const observer = new ResizeObserver(() => {
|
|
9479
|
+
if (element.clientHeight === 0) {
|
|
9480
|
+
suspended = true;
|
|
9481
|
+
cancelAnimationFrame(frame);
|
|
9482
|
+
return;
|
|
9483
|
+
}
|
|
9484
|
+
if (!suspended || !anchor) {
|
|
9485
|
+
suspended = false;
|
|
9486
|
+
remember();
|
|
9487
|
+
return;
|
|
9488
|
+
}
|
|
9489
|
+
let attempts = 0;
|
|
9490
|
+
let stableFrames = 0;
|
|
9491
|
+
const restore = () => {
|
|
9492
|
+
if (!anchor || element.clientHeight === 0) return;
|
|
9493
|
+
const index = rowKeysRef.current.indexOf(anchor.key);
|
|
9494
|
+
if (index < 0) {
|
|
9495
|
+
anchor = null;
|
|
9496
|
+
suspended = false;
|
|
9497
|
+
return;
|
|
9498
|
+
}
|
|
9499
|
+
const row = visibleRows().find((row) => row.dataset.scrollAnchor === anchor.key);
|
|
9500
|
+
if (row) {
|
|
9501
|
+
const delta = row.getBoundingClientRect().top - element.getBoundingClientRect().top - anchor.offset;
|
|
9502
|
+
element.scrollTop += delta;
|
|
9503
|
+
stableFrames = Math.abs(delta) < 1 ? stableFrames + 1 : 0;
|
|
9504
|
+
} else {
|
|
9505
|
+
const offset = virtualizer.getOffsetForIndex(index, "start")?.[0];
|
|
9506
|
+
if (offset !== void 0) virtualizer.scrollToOffset(offset - anchor.offset);
|
|
9507
|
+
stableFrames = 0;
|
|
9508
|
+
}
|
|
9509
|
+
if (++attempts < 60 && stableFrames < 3) frame = requestAnimationFrame(restore);
|
|
9510
|
+
else {
|
|
9511
|
+
suspended = false;
|
|
9512
|
+
remember();
|
|
9513
|
+
}
|
|
9514
|
+
};
|
|
9515
|
+
frame = requestAnimationFrame(restore);
|
|
9516
|
+
});
|
|
9517
|
+
const interrupt = () => {
|
|
9518
|
+
cancelAnimationFrame(frame);
|
|
9519
|
+
anchor = null;
|
|
9520
|
+
suspended = false;
|
|
9521
|
+
};
|
|
9522
|
+
element.addEventListener("scroll", remember, { passive: true });
|
|
9523
|
+
element.addEventListener("wheel", interrupt, { passive: true });
|
|
9524
|
+
element.addEventListener("touchstart", interrupt, { passive: true });
|
|
9525
|
+
element.addEventListener("keydown", interrupt);
|
|
9526
|
+
observer.observe(element);
|
|
9527
|
+
remember();
|
|
9528
|
+
return () => {
|
|
9529
|
+
cancelAnimationFrame(frame);
|
|
9530
|
+
observer.disconnect();
|
|
9531
|
+
element.removeEventListener("scroll", remember);
|
|
9532
|
+
element.removeEventListener("wheel", interrupt);
|
|
9533
|
+
element.removeEventListener("touchstart", interrupt);
|
|
9534
|
+
element.removeEventListener("keydown", interrupt);
|
|
9535
|
+
};
|
|
9536
|
+
}, [
|
|
9537
|
+
scrollElementFromCtx,
|
|
9538
|
+
virtualizer,
|
|
9539
|
+
firstMessageId
|
|
9540
|
+
]);
|
|
8069
9541
|
const messageElements = shouldVirtualize ? [] : rows.flatMap(renderRow);
|
|
8070
|
-
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
9542
|
+
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageFeedbackScopeContext.Provider, {
|
|
9543
|
+
value: feedbackScope,
|
|
9544
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallDisplayContext.Provider, {
|
|
9545
|
+
value: toolCallDisplayContext,
|
|
9546
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9547
|
+
"data-copilotkit": true,
|
|
9548
|
+
style: { display: "contents" },
|
|
9549
|
+
children: children({
|
|
9550
|
+
messageElements,
|
|
9551
|
+
messages,
|
|
9552
|
+
isRunning,
|
|
9553
|
+
interruptElement
|
|
9554
|
+
})
|
|
9555
|
+
})
|
|
8078
9556
|
})
|
|
8079
9557
|
});
|
|
8080
9558
|
const lastMessage = renderedMessages[renderedMessages.length - 1];
|
|
8081
|
-
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
const row = rows[virtualItem.index];
|
|
8095
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8096
|
-
"data-index": virtualItem.index,
|
|
8097
|
-
ref: virtualizer.measureElement,
|
|
9559
|
+
const showCursor = isRunning && lastMessage?.role !== "reasoning" && toolCallDisplay !== "turn";
|
|
9560
|
+
const pendingWorkSummary = toolCallDisplay === "turn" && isRunning && lastMessage?.role === "user";
|
|
9561
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageFeedbackScopeContext.Provider, {
|
|
9562
|
+
value: feedbackScope,
|
|
9563
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallDisplayContext.Provider, {
|
|
9564
|
+
value: toolCallDisplayContext,
|
|
9565
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9566
|
+
"data-copilotkit": true,
|
|
9567
|
+
"data-testid": "copilot-message-list",
|
|
9568
|
+
className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
9569
|
+
...props,
|
|
9570
|
+
children: [
|
|
9571
|
+
shouldVirtualize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8098
9572
|
style: {
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
left: 0,
|
|
8102
|
-
width: "100%",
|
|
8103
|
-
transform: `translateY(${virtualItem.start}px)`
|
|
9573
|
+
height: virtualizer.getTotalSize(),
|
|
9574
|
+
position: "relative"
|
|
8104
9575
|
},
|
|
8105
|
-
children:
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
9576
|
+
children: virtualizer.getVirtualItems().map((virtualItem) => {
|
|
9577
|
+
const row = rows[virtualItem.index];
|
|
9578
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9579
|
+
"data-index": virtualItem.index,
|
|
9580
|
+
"data-scroll-anchor": rowKey(row),
|
|
9581
|
+
ref: virtualizer.measureElement,
|
|
9582
|
+
style: {
|
|
9583
|
+
position: "absolute",
|
|
9584
|
+
top: 0,
|
|
9585
|
+
left: 0,
|
|
9586
|
+
width: "100%",
|
|
9587
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
9588
|
+
},
|
|
9589
|
+
children: renderRow(row)
|
|
9590
|
+
}, rowKey(row));
|
|
9591
|
+
})
|
|
9592
|
+
}) : messageElements,
|
|
9593
|
+
interruptElement,
|
|
9594
|
+
pendingWorkSummary && renderWorkSummary(latestTurnKey, []),
|
|
9595
|
+
showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9596
|
+
className: "cpk:mt-2",
|
|
9597
|
+
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
|
|
9598
|
+
})
|
|
9599
|
+
]
|
|
8113
9600
|
})
|
|
8114
|
-
|
|
9601
|
+
})
|
|
8115
9602
|
});
|
|
8116
9603
|
}
|
|
8117
9604
|
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
@@ -8496,6 +9983,16 @@ function DropOverlay() {
|
|
|
8496
9983
|
});
|
|
8497
9984
|
}
|
|
8498
9985
|
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, intelligenceIndicator, children, className, ...props }) {
|
|
9986
|
+
const configuredThreadId = useCopilotChatConfiguration()?.threadId;
|
|
9987
|
+
const [fallbackThreadId, setFallbackThreadId] = (0, react.useState)(messages[0]?.id);
|
|
9988
|
+
const previousMessageIds = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
9989
|
+
(0, react.useEffect)(() => {
|
|
9990
|
+
if (messages.length === 0) return;
|
|
9991
|
+
const overlaps = messages.some((message) => previousMessageIds.current.has(message.id));
|
|
9992
|
+
previousMessageIds.current = new Set(messages.map((message) => message.id));
|
|
9993
|
+
if (!overlaps) setFallbackThreadId(messages[0].id);
|
|
9994
|
+
}, [messages]);
|
|
9995
|
+
const scrollThreadId = configuredThreadId ?? fallbackThreadId;
|
|
8499
9996
|
const [inputContainerEl, setInputContainerEl] = (0, react.useState)(null);
|
|
8500
9997
|
const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
|
|
8501
9998
|
const [isResizing, setIsResizing] = (0, react.useState)(false);
|
|
@@ -8562,6 +10059,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
8562
10059
|
}) : null;
|
|
8563
10060
|
const BoundScrollView = renderSlot(scrollView, CopilotChatView.ScrollView, {
|
|
8564
10061
|
autoScroll,
|
|
10062
|
+
scrollThreadId,
|
|
8565
10063
|
inputContainerHeight,
|
|
8566
10064
|
isResizing,
|
|
8567
10065
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -8657,12 +10155,102 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
8657
10155
|
});
|
|
8658
10156
|
}
|
|
8659
10157
|
(function(_CopilotChatView) {
|
|
8660
|
-
const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
|
|
8661
|
-
const { isAtBottom, scrollToBottom, scrollRef, state } = (0, use_stick_to_bottom.useStickToBottomContext)();
|
|
10158
|
+
const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing, scrollThreadId }) => {
|
|
10159
|
+
const { isAtBottom, scrollToBottom, stopScroll, scrollRef, state } = (0, use_stick_to_bottom.useStickToBottomContext)();
|
|
8662
10160
|
const [scrollEl, setScrollEl] = (0, react.useState)(null);
|
|
10161
|
+
const readingOffsetRef = (0, react.useRef)(null);
|
|
10162
|
+
const pinIntentRef = (0, react.useRef)(false);
|
|
10163
|
+
const stateRef = (0, react.useRef)(state);
|
|
10164
|
+
stateRef.current = state;
|
|
8663
10165
|
(0, react.useLayoutEffect)(() => {
|
|
8664
10166
|
setScrollEl(scrollRef.current ?? null);
|
|
8665
10167
|
}, []);
|
|
10168
|
+
(0, react.useEffect)(() => {
|
|
10169
|
+
if (!scrollEl) return;
|
|
10170
|
+
let hidden = false;
|
|
10171
|
+
let resumeBottom = false;
|
|
10172
|
+
let pendingFrame = 0;
|
|
10173
|
+
let resumeFrame = 0;
|
|
10174
|
+
let settleFrame = 0;
|
|
10175
|
+
readingOffsetRef.current = null;
|
|
10176
|
+
const rememberScroll = () => {
|
|
10177
|
+
if (hidden || scrollEl.clientHeight === 0) return;
|
|
10178
|
+
if (resumeBottom) {
|
|
10179
|
+
readingOffsetRef.current = null;
|
|
10180
|
+
return;
|
|
10181
|
+
}
|
|
10182
|
+
if (pinIntentRef.current) if (stateRef.current.escapedFromLock) pinIntentRef.current = false;
|
|
10183
|
+
else {
|
|
10184
|
+
readingOffsetRef.current = null;
|
|
10185
|
+
if (!stateRef.current.isNearBottom) return;
|
|
10186
|
+
pinIntentRef.current = false;
|
|
10187
|
+
}
|
|
10188
|
+
readingOffsetRef.current = !stateRef.current.isAtBottom && (stateRef.current.escapedFromLock || !stateRef.current.isNearBottom) ? scrollEl.scrollTop : null;
|
|
10189
|
+
};
|
|
10190
|
+
const onScroll = () => {
|
|
10191
|
+
rememberScroll();
|
|
10192
|
+
cancelAnimationFrame(pendingFrame);
|
|
10193
|
+
pendingFrame = requestAnimationFrame(rememberScroll);
|
|
10194
|
+
};
|
|
10195
|
+
const onWheel = (event) => {
|
|
10196
|
+
if (event.deltaY < 0) {
|
|
10197
|
+
pinIntentRef.current = false;
|
|
10198
|
+
resumeBottom = false;
|
|
10199
|
+
}
|
|
10200
|
+
};
|
|
10201
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
10202
|
+
if (scrollEl.clientHeight === 0) {
|
|
10203
|
+
hidden = true;
|
|
10204
|
+
resumeBottom = pinIntentRef.current || readingOffsetRef.current === null;
|
|
10205
|
+
if (!resumeBottom) stopScroll();
|
|
10206
|
+
} else if (hidden) {
|
|
10207
|
+
hidden = false;
|
|
10208
|
+
if (resumeBottom) {
|
|
10209
|
+
stopScroll();
|
|
10210
|
+
resumeFrame = requestAnimationFrame(() => {
|
|
10211
|
+
settleFrame = requestAnimationFrame(() => {
|
|
10212
|
+
if (scrollEl.clientHeight > 0 && resumeBottom) {
|
|
10213
|
+
resumeBottom = false;
|
|
10214
|
+
pinIntentRef.current = true;
|
|
10215
|
+
scrollToBottom({ animation: "instant" });
|
|
10216
|
+
}
|
|
10217
|
+
});
|
|
10218
|
+
});
|
|
10219
|
+
} else if (readingOffsetRef.current !== null) {
|
|
10220
|
+
stopScroll();
|
|
10221
|
+
scrollEl.scrollTop = readingOffsetRef.current;
|
|
10222
|
+
}
|
|
10223
|
+
}
|
|
10224
|
+
});
|
|
10225
|
+
rememberScroll();
|
|
10226
|
+
scrollEl.addEventListener("scroll", onScroll);
|
|
10227
|
+
scrollEl.addEventListener("wheel", onWheel, { passive: true });
|
|
10228
|
+
resizeObserver.observe(scrollEl);
|
|
10229
|
+
return () => {
|
|
10230
|
+
scrollEl.removeEventListener("scroll", onScroll);
|
|
10231
|
+
scrollEl.removeEventListener("wheel", onWheel);
|
|
10232
|
+
cancelAnimationFrame(pendingFrame);
|
|
10233
|
+
cancelAnimationFrame(resumeFrame);
|
|
10234
|
+
cancelAnimationFrame(settleFrame);
|
|
10235
|
+
resizeObserver.disconnect();
|
|
10236
|
+
};
|
|
10237
|
+
}, [
|
|
10238
|
+
scrollEl,
|
|
10239
|
+
scrollToBottom,
|
|
10240
|
+
stopScroll,
|
|
10241
|
+
scrollThreadId
|
|
10242
|
+
]);
|
|
10243
|
+
const startBottomPin = (0, react.useCallback)((instant = false) => {
|
|
10244
|
+
readingOffsetRef.current = null;
|
|
10245
|
+
pinIntentRef.current = true;
|
|
10246
|
+
scrollToBottom(instant ? { animation: "instant" } : void 0);
|
|
10247
|
+
}, [scrollToBottom]);
|
|
10248
|
+
const previousScrollThreadId = (0, react.useRef)(scrollThreadId);
|
|
10249
|
+
(0, react.useEffect)(() => {
|
|
10250
|
+
if (previousScrollThreadId.current === scrollThreadId) return;
|
|
10251
|
+
previousScrollThreadId.current = scrollThreadId;
|
|
10252
|
+
startBottomPin(true);
|
|
10253
|
+
}, [scrollThreadId, startBottomPin]);
|
|
8666
10254
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
8667
10255
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
|
|
8668
10256
|
value: scrollEl,
|
|
@@ -8684,7 +10272,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
8684
10272
|
!isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8685
10273
|
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
8686
10274
|
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
|
|
8687
|
-
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () =>
|
|
10275
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => startBottomPin() })
|
|
8688
10276
|
})
|
|
8689
10277
|
] })
|
|
8690
10278
|
})
|
|
@@ -8732,7 +10320,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
8732
10320
|
})
|
|
8733
10321
|
});
|
|
8734
10322
|
};
|
|
8735
|
-
_CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
|
|
10323
|
+
_CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollThreadId, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
|
|
8736
10324
|
const mode = normalizeAutoScroll(autoScroll);
|
|
8737
10325
|
const [hasMounted, setHasMounted] = (0, react.useState)(false);
|
|
8738
10326
|
const scrollRef = (0, react.useRef)(null);
|
|
@@ -8821,6 +10409,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
8821
10409
|
initial: "smooth",
|
|
8822
10410
|
...props,
|
|
8823
10411
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollContent, {
|
|
10412
|
+
scrollThreadId,
|
|
8824
10413
|
scrollToBottomButton,
|
|
8825
10414
|
feather,
|
|
8826
10415
|
inputContainerHeight,
|
|
@@ -8829,14 +10418,18 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
8829
10418
|
})
|
|
8830
10419
|
});
|
|
8831
10420
|
};
|
|
8832
|
-
_CopilotChatView.ScrollToBottomButton = ({ className, ...props }) =>
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
10421
|
+
_CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => {
|
|
10422
|
+
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
10423
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
10424
|
+
"data-testid": "copilot-scroll-to-bottom",
|
|
10425
|
+
variant: "outline",
|
|
10426
|
+
size: "sm",
|
|
10427
|
+
className: (0, tailwind_merge.twMerge)("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className),
|
|
10428
|
+
"aria-label": labels.chatScrollToBottomLabel,
|
|
10429
|
+
...props,
|
|
10430
|
+
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" })
|
|
10431
|
+
});
|
|
10432
|
+
};
|
|
8840
10433
|
_CopilotChatView.Feather = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8841
10434
|
className,
|
|
8842
10435
|
...props
|
|
@@ -10219,6 +11812,7 @@ function CopilotPopupViewInternal({ header, toggleButton, width, height, clickOu
|
|
|
10219
11812
|
if (containerRef.current?.contains(target)) return;
|
|
10220
11813
|
const toggleButton = document.querySelector("[data-slot='chat-toggle-button']");
|
|
10221
11814
|
if (toggleButton && toggleButton.contains(target)) return;
|
|
11815
|
+
if (target instanceof Element && target.closest("[data-copilotkit-popover]")) return;
|
|
10222
11816
|
setModalOpen?.(false);
|
|
10223
11817
|
};
|
|
10224
11818
|
document.addEventListener("pointerdown", handlePointerDown);
|
|
@@ -10729,62 +12323,14 @@ CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
|
|
|
10729
12323
|
|
|
10730
12324
|
//#endregion
|
|
10731
12325
|
//#region src/v2/components/WildcardToolCallRender.tsx
|
|
12326
|
+
/**
|
|
12327
|
+
* Catch-all (`"*"`) tool-call renderer that shows CopilotKit's default
|
|
12328
|
+
* tool-call card for any tool without a dedicated renderer.
|
|
12329
|
+
*/
|
|
10732
12330
|
const WildcardToolCallRender = defineToolCallRenderer({
|
|
10733
12331
|
name: "*",
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
const statusString = String(status);
|
|
10737
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10738
|
-
className: "cpk:mt-2 cpk:pb-2",
|
|
10739
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10740
|
-
className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:dark:border-zinc-800/60 cpk:bg-white/70 cpk:dark:bg-zinc-900/50 cpk:shadow-sm cpk:backdrop-blur cpk:p-4",
|
|
10741
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10742
|
-
className: "cpk:flex cpk:items-center cpk:justify-between cpk:gap-3 cpk:cursor-pointer",
|
|
10743
|
-
onClick: () => setIsExpanded(!isExpanded),
|
|
10744
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10745
|
-
className: "cpk:flex cpk:items-center cpk:gap-2 cpk:min-w-0",
|
|
10746
|
-
children: [
|
|
10747
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
10748
|
-
className: `cpk:h-4 cpk:w-4 cpk:text-zinc-500 cpk:dark:text-zinc-400 cpk:transition-transform ${isExpanded ? "cpk:rotate-90" : ""}`,
|
|
10749
|
-
fill: "none",
|
|
10750
|
-
viewBox: "0 0 24 24",
|
|
10751
|
-
strokeWidth: 2,
|
|
10752
|
-
stroke: "currentColor",
|
|
10753
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10754
|
-
strokeLinecap: "round",
|
|
10755
|
-
strokeLinejoin: "round",
|
|
10756
|
-
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
|
|
10757
|
-
})
|
|
10758
|
-
}),
|
|
10759
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:inline-block cpk:h-2 cpk:w-2 cpk:rounded-full cpk:bg-blue-500" }),
|
|
10760
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10761
|
-
className: "cpk:truncate cpk:text-sm cpk:font-medium cpk:text-zinc-900 cpk:dark:text-zinc-100",
|
|
10762
|
-
children: name
|
|
10763
|
-
})
|
|
10764
|
-
]
|
|
10765
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10766
|
-
className: `cpk:inline-flex cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-1 cpk:text-xs cpk:font-medium ${statusString === "inProgress" || statusString === "executing" ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : statusString === "complete" ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300"}`,
|
|
10767
|
-
children: String(status)
|
|
10768
|
-
})]
|
|
10769
|
-
}), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10770
|
-
className: "cpk:mt-3 cpk:grid cpk:gap-4",
|
|
10771
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10772
|
-
className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
10773
|
-
children: "Arguments"
|
|
10774
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
10775
|
-
className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
|
|
10776
|
-
children: JSON.stringify(args ?? {}, null, 2)
|
|
10777
|
-
})] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10778
|
-
className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
10779
|
-
children: "Result"
|
|
10780
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
10781
|
-
className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
|
|
10782
|
-
children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
10783
|
-
})] })]
|
|
10784
|
-
})]
|
|
10785
|
-
})
|
|
10786
|
-
});
|
|
10787
|
-
}
|
|
12332
|
+
group: true,
|
|
12333
|
+
render: (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(props) })
|
|
10788
12334
|
});
|
|
10789
12335
|
|
|
10790
12336
|
//#endregion
|
|
@@ -13063,6 +14609,12 @@ Object.defineProperty(exports, 'CopilotChatToggleButton', {
|
|
|
13063
14609
|
return CopilotChatToggleButton;
|
|
13064
14610
|
}
|
|
13065
14611
|
});
|
|
14612
|
+
Object.defineProperty(exports, 'CopilotChatToolCallGroup', {
|
|
14613
|
+
enumerable: true,
|
|
14614
|
+
get: function () {
|
|
14615
|
+
return CopilotChatToolCallGroup;
|
|
14616
|
+
}
|
|
14617
|
+
});
|
|
13066
14618
|
Object.defineProperty(exports, 'CopilotChatToolCallsView', {
|
|
13067
14619
|
enumerable: true,
|
|
13068
14620
|
get: function () {
|
|
@@ -13081,6 +14633,12 @@ Object.defineProperty(exports, 'CopilotChatView_default', {
|
|
|
13081
14633
|
return CopilotChatView_default;
|
|
13082
14634
|
}
|
|
13083
14635
|
});
|
|
14636
|
+
Object.defineProperty(exports, 'CopilotChatWorkSummary', {
|
|
14637
|
+
enumerable: true,
|
|
14638
|
+
get: function () {
|
|
14639
|
+
return CopilotChatWorkSummary;
|
|
14640
|
+
}
|
|
14641
|
+
});
|
|
13084
14642
|
Object.defineProperty(exports, 'CopilotContext', {
|
|
13085
14643
|
enumerable: true,
|
|
13086
14644
|
get: function () {
|
|
@@ -13483,4 +15041,4 @@ Object.defineProperty(exports, 'useToast', {
|
|
|
13483
15041
|
return useToast;
|
|
13484
15042
|
}
|
|
13485
15043
|
});
|
|
13486
|
-
//# sourceMappingURL=copilotkit-
|
|
15044
|
+
//# sourceMappingURL=copilotkit-DKICi3nC.cjs.map
|