@copilotkit/react-core 1.55.0-next.8 → 1.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -5
- package/dist/{copilotkit-DNYSFuz5.mjs → copilotkit-BY5S1-0P.mjs} +2772 -858
- package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
- package/dist/{copilotkit-Dy5w3qEV.d.mts → copilotkit-BuhSUZHb.d.mts} +230 -17
- package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
- package/dist/{copilotkit-B3Mb1yVE.cjs → copilotkit-Bz5-ImDl.cjs} +2776 -832
- package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
- package/dist/{copilotkit-DBzgOMby.d.cts → copilotkit-dwDWYpya.d.cts} +230 -17
- package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +9 -4
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1624 -396
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +13 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +3 -3
- package/dist/v2/index.d.mts +3 -3
- package/dist/v2/index.mjs +3 -2
- package/dist/v2/index.umd.js +2746 -790
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +62 -54
- package/scripts/scope-preflight.mjs +1 -2
- package/src/components/CopilotListeners.tsx +41 -8
- package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +92 -0
- package/src/components/copilot-provider/copilotkit-props.tsx +4 -2
- package/src/components/copilot-provider/copilotkit.tsx +3 -3
- package/src/components/toast/toast-provider.tsx +269 -194
- package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +27 -16
- package/src/hooks/use-copilot-chat_internal.ts +15 -4
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
- package/src/v2/__tests__/utils/test-helpers.tsx +107 -7
- package/src/v2/a2ui/A2UICatalogContext.tsx +79 -0
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +125 -37
- package/src/v2/a2ui/A2UIToolCallRenderer.tsx +290 -0
- package/src/v2/components/CopilotKitInspector.tsx +2 -0
- package/src/v2/components/OpenGenerativeUIRenderer.tsx +598 -0
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +665 -0
- package/src/v2/components/chat/CopilotChat.tsx +197 -52
- package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +17 -2
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +481 -0
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +139 -0
- package/src/v2/components/chat/CopilotChatInput.tsx +146 -77
- package/src/v2/components/chat/CopilotChatMessageView.tsx +260 -151
- package/src/v2/components/chat/CopilotChatSuggestionView.tsx +1 -0
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +54 -0
- package/src/v2/components/chat/CopilotChatView.tsx +179 -66
- package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +168 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +63 -2
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +544 -1
- package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +268 -0
- package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +249 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +60 -3
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +138 -0
- package/src/v2/components/chat/index.ts +9 -0
- package/src/v2/components/chat/scroll-element-context.ts +13 -0
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +8 -0
- package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +327 -0
- package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +1003 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +13 -2
- package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +23 -4
- package/src/v2/hooks/__tests__/use-threads.test.tsx +54 -0
- package/src/v2/hooks/index.ts +5 -0
- package/src/v2/hooks/use-agent.tsx +220 -15
- package/src/v2/hooks/use-attachments.tsx +269 -0
- package/src/v2/hooks/use-frontend-tool.tsx +5 -2
- package/src/v2/hooks/use-render-activity-message.tsx +9 -2
- package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
- package/src/v2/hooks/use-threads.tsx +35 -15
- package/src/v2/index.ts +5 -1
- package/src/v2/lib/__tests__/processPartialHtml.test.ts +112 -0
- package/src/v2/lib/__tests__/slots.test.ts +56 -0
- package/src/v2/lib/processPartialHtml.ts +45 -0
- package/src/v2/lib/slots.tsx +42 -1
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +9 -3
- package/src/v2/providers/CopilotKitProvider.tsx +268 -32
- package/src/v2/providers/SandboxFunctionsContext.ts +10 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +198 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +71 -0
- package/src/v2/providers/index.ts +7 -0
- package/src/v2/styles/globals.css +2 -1
- package/src/v2/types/index.ts +1 -0
- package/src/v2/types/sandbox-function.ts +11 -0
- package/dist/copilotkit-B3Mb1yVE.cjs.map +0 -1
- package/dist/copilotkit-DBzgOMby.d.cts.map +0 -1
- package/dist/copilotkit-DNYSFuz5.mjs.map +0 -1
- package/dist/copilotkit-Dy5w3qEV.d.mts.map +0 -1
- package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
|
@@ -125,7 +125,7 @@ export function CopilotChatInput({
|
|
|
125
125
|
onChange,
|
|
126
126
|
value,
|
|
127
127
|
toolsMenu,
|
|
128
|
-
autoFocus =
|
|
128
|
+
autoFocus = false,
|
|
129
129
|
positioning = "static",
|
|
130
130
|
keyboardHeight = 0,
|
|
131
131
|
containerRef,
|
|
@@ -179,6 +179,12 @@ export function CopilotChatInput({
|
|
|
179
179
|
paddingRight: 0,
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
// Cached container dimensions — invalidated on resize, lazily repopulated on next layout pass.
|
|
183
|
+
// Eliminates getComputedStyle(grid) + 2x getBoundingClientRect per compact-layout evaluation.
|
|
184
|
+
const containerCacheRef = useRef<{
|
|
185
|
+
compactWidth: number;
|
|
186
|
+
} | null>(null);
|
|
187
|
+
|
|
182
188
|
const commandItems = useMemo(() => {
|
|
183
189
|
const entries: ToolsMenuItem[] = [];
|
|
184
190
|
const seen = new Set<string>();
|
|
@@ -252,7 +258,7 @@ export function CopilotChatInput({
|
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
if (config?.isModalOpen && !previousModalStateRef.current) {
|
|
255
|
-
inputRef.current?.focus();
|
|
261
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
256
262
|
}
|
|
257
263
|
|
|
258
264
|
previousModalStateRef.current = config?.isModalOpen;
|
|
@@ -677,6 +683,36 @@ export function CopilotChatInput({
|
|
|
677
683
|
});
|
|
678
684
|
}, []);
|
|
679
685
|
|
|
686
|
+
const updateContainerCache = useCallback((): {
|
|
687
|
+
compactWidth: number;
|
|
688
|
+
} | null => {
|
|
689
|
+
const grid = gridRef.current;
|
|
690
|
+
const addContainer = addButtonContainerRef.current;
|
|
691
|
+
const actionsContainer = actionsContainerRef.current;
|
|
692
|
+
if (!grid || !addContainer || !actionsContainer) return null;
|
|
693
|
+
|
|
694
|
+
const gridStyles = window.getComputedStyle(grid);
|
|
695
|
+
const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
|
|
696
|
+
const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
|
|
697
|
+
const columnGap = parseFloat(gridStyles.columnGap) || 0;
|
|
698
|
+
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
699
|
+
|
|
700
|
+
if (gridAvailableWidth <= 0) return null;
|
|
701
|
+
|
|
702
|
+
const addWidth = addContainer.getBoundingClientRect().width;
|
|
703
|
+
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
704
|
+
const compactWidth = Math.max(
|
|
705
|
+
gridAvailableWidth - addWidth - actionsWidth - columnGap * 2,
|
|
706
|
+
0,
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
if (compactWidth <= 0) return null;
|
|
710
|
+
|
|
711
|
+
const result = { compactWidth };
|
|
712
|
+
containerCacheRef.current = result;
|
|
713
|
+
return result;
|
|
714
|
+
}, []);
|
|
715
|
+
|
|
680
716
|
const evaluateLayout = useCallback(() => {
|
|
681
717
|
if (mode !== "input") {
|
|
682
718
|
updateLayout("compact");
|
|
@@ -717,55 +753,71 @@ export function CopilotChatInput({
|
|
|
717
753
|
let shouldExpand = hasExplicitBreak || renderedMultiline;
|
|
718
754
|
|
|
719
755
|
if (!shouldExpand) {
|
|
720
|
-
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
729
|
-
const compactWidth = Math.max(
|
|
730
|
-
gridAvailableWidth - addWidth - actionsWidth - columnGap * 2,
|
|
756
|
+
// Use cached container dimensions (lazily populated on first access, invalidated on resize).
|
|
757
|
+
const cache = containerCacheRef.current ?? updateContainerCache();
|
|
758
|
+
|
|
759
|
+
if (cache && cache.compactWidth > 0) {
|
|
760
|
+
const compactInnerWidth = Math.max(
|
|
761
|
+
cache.compactWidth -
|
|
762
|
+
(measurementsRef.current.paddingLeft || 0) -
|
|
763
|
+
(measurementsRef.current.paddingRight || 0),
|
|
731
764
|
0,
|
|
732
765
|
);
|
|
733
766
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
measurementCanvasRef.current = canvas;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
const context = canvas.getContext("2d");
|
|
741
|
-
if (context) {
|
|
767
|
+
if (compactInnerWidth > 0) {
|
|
768
|
+
// Read font fresh each evaluation — getComputedStyle for style-only
|
|
769
|
+
// properties is cheap and avoids stale values after CSS/theme changes.
|
|
742
770
|
const textareaStyles = window.getComputedStyle(textarea);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
771
|
+
let font = textareaStyles.font;
|
|
772
|
+
if (!font) {
|
|
773
|
+
const {
|
|
774
|
+
fontStyle,
|
|
775
|
+
fontVariant,
|
|
776
|
+
fontWeight,
|
|
777
|
+
fontSize,
|
|
778
|
+
lineHeight,
|
|
779
|
+
fontFamily,
|
|
780
|
+
} = textareaStyles;
|
|
781
|
+
if (fontSize && fontFamily) {
|
|
782
|
+
font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
754
785
|
|
|
755
|
-
if (
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const metrics = context.measureText(line || " ");
|
|
761
|
-
if (metrics.width > longestWidth) {
|
|
762
|
-
longestWidth = metrics.width;
|
|
763
|
-
}
|
|
786
|
+
if (font?.trim()) {
|
|
787
|
+
const canvas =
|
|
788
|
+
measurementCanvasRef.current ?? document.createElement("canvas");
|
|
789
|
+
if (!measurementCanvasRef.current) {
|
|
790
|
+
measurementCanvasRef.current = canvas;
|
|
764
791
|
}
|
|
765
792
|
|
|
766
|
-
|
|
767
|
-
|
|
793
|
+
const context = canvas.getContext("2d");
|
|
794
|
+
if (context) {
|
|
795
|
+
context.font = font;
|
|
796
|
+
|
|
797
|
+
const lines =
|
|
798
|
+
resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
|
|
799
|
+
let longestWidth = 0;
|
|
800
|
+
for (const line of lines) {
|
|
801
|
+
const metrics = context.measureText(line || " ");
|
|
802
|
+
if (metrics.width > longestWidth) {
|
|
803
|
+
longestWidth = metrics.width;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (longestWidth > compactInnerWidth) {
|
|
808
|
+
shouldExpand = true;
|
|
809
|
+
}
|
|
810
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
811
|
+
console.warn(
|
|
812
|
+
"[CopilotChatInput] canvas.getContext('2d') returned null. " +
|
|
813
|
+
"Text-width-based expansion will be unavailable.",
|
|
814
|
+
);
|
|
768
815
|
}
|
|
816
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
817
|
+
console.warn(
|
|
818
|
+
"[CopilotChatInput] Could not resolve textarea font for layout measurement. " +
|
|
819
|
+
"Text-width-based expansion will be skipped until the next evaluation.",
|
|
820
|
+
);
|
|
769
821
|
}
|
|
770
822
|
}
|
|
771
823
|
}
|
|
@@ -778,6 +830,7 @@ export function CopilotChatInput({
|
|
|
778
830
|
ensureMeasurements,
|
|
779
831
|
mode,
|
|
780
832
|
resolvedValue,
|
|
833
|
+
updateContainerCache,
|
|
781
834
|
updateLayout,
|
|
782
835
|
]);
|
|
783
836
|
|
|
@@ -799,12 +852,24 @@ export function CopilotChatInput({
|
|
|
799
852
|
return;
|
|
800
853
|
}
|
|
801
854
|
|
|
802
|
-
const
|
|
855
|
+
const containerTargets = new Set<Element>([
|
|
856
|
+
grid,
|
|
857
|
+
addContainer,
|
|
858
|
+
actionsContainer,
|
|
859
|
+
]);
|
|
860
|
+
|
|
861
|
+
const scheduleEvaluation = (invalidateCache: boolean) => {
|
|
803
862
|
if (ignoreResizeRef.current) {
|
|
804
863
|
ignoreResizeRef.current = false;
|
|
864
|
+
// Self-inflicted resize from a layout toggle — container dimensions
|
|
865
|
+
// are unchanged, so keep the cache warm.
|
|
805
866
|
return;
|
|
806
867
|
}
|
|
807
868
|
|
|
869
|
+
if (invalidateCache) {
|
|
870
|
+
containerCacheRef.current = null;
|
|
871
|
+
}
|
|
872
|
+
|
|
808
873
|
if (typeof window === "undefined") {
|
|
809
874
|
evaluateLayout();
|
|
810
875
|
return;
|
|
@@ -820,8 +885,19 @@ export function CopilotChatInput({
|
|
|
820
885
|
});
|
|
821
886
|
};
|
|
822
887
|
|
|
823
|
-
|
|
824
|
-
|
|
888
|
+
// Single observer for all elements — inspect entry.target to decide
|
|
889
|
+
// whether to invalidate the container dimension cache. Container
|
|
890
|
+
// targets (grid, buttons) changing size means compactWidth may have
|
|
891
|
+
// changed; textarea height changes (typing) do not affect it.
|
|
892
|
+
const observer = new ResizeObserver((entries) => {
|
|
893
|
+
let shouldInvalidate = false;
|
|
894
|
+
for (const entry of entries) {
|
|
895
|
+
if (containerTargets.has(entry.target)) {
|
|
896
|
+
shouldInvalidate = true;
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
scheduleEvaluation(shouldInvalidate);
|
|
825
901
|
});
|
|
826
902
|
|
|
827
903
|
observer.observe(grid);
|
|
@@ -1106,6 +1182,10 @@ export namespace CopilotChatInput {
|
|
|
1106
1182
|
const config = useCopilotChatConfiguration();
|
|
1107
1183
|
const labels = config?.labels ?? CopilotChatDefaultLabels;
|
|
1108
1184
|
|
|
1185
|
+
// Defer Radix UI rendering until after hydration to avoid ID mismatches
|
|
1186
|
+
const [mounted, setMounted] = useState(false);
|
|
1187
|
+
useEffect(() => setMounted(true), []);
|
|
1188
|
+
|
|
1109
1189
|
const menuItems = useMemo<(ToolsMenuItem | "-")[]>(() => {
|
|
1110
1190
|
const items: (ToolsMenuItem | "-")[] = [];
|
|
1111
1191
|
|
|
@@ -1170,27 +1250,32 @@ export namespace CopilotChatInput {
|
|
|
1170
1250
|
const hasMenuItems = menuItems.length > 0;
|
|
1171
1251
|
const isDisabled = disabled || !hasMenuItems;
|
|
1172
1252
|
|
|
1253
|
+
const button = (
|
|
1254
|
+
<Button
|
|
1255
|
+
type="button"
|
|
1256
|
+
data-testid="copilot-add-menu-button"
|
|
1257
|
+
variant="chatInputToolbarSecondary"
|
|
1258
|
+
size="chatInputToolbarIcon"
|
|
1259
|
+
className={twMerge("cpk:ml-1", className)}
|
|
1260
|
+
disabled={isDisabled}
|
|
1261
|
+
{...props}
|
|
1262
|
+
>
|
|
1263
|
+
<Plus className="cpk:size-[20px]" />
|
|
1264
|
+
</Button>
|
|
1265
|
+
);
|
|
1266
|
+
|
|
1267
|
+
// Render plain button during SSR; Radix wrappers only after hydration
|
|
1268
|
+
if (!mounted) return button;
|
|
1269
|
+
|
|
1173
1270
|
return (
|
|
1174
1271
|
<DropdownMenu>
|
|
1175
1272
|
<Tooltip>
|
|
1176
1273
|
<TooltipTrigger asChild>
|
|
1177
|
-
<DropdownMenuTrigger asChild>
|
|
1178
|
-
<Button
|
|
1179
|
-
type="button"
|
|
1180
|
-
data-testid="copilot-add-menu-button"
|
|
1181
|
-
variant="chatInputToolbarSecondary"
|
|
1182
|
-
size="chatInputToolbarIcon"
|
|
1183
|
-
className={twMerge("cpk:ml-1", className)}
|
|
1184
|
-
disabled={isDisabled}
|
|
1185
|
-
{...props}
|
|
1186
|
-
>
|
|
1187
|
-
<Plus className="cpk:size-[20px]" />
|
|
1188
|
-
</Button>
|
|
1189
|
-
</DropdownMenuTrigger>
|
|
1274
|
+
<DropdownMenuTrigger asChild>{button}</DropdownMenuTrigger>
|
|
1190
1275
|
</TooltipTrigger>
|
|
1191
1276
|
<TooltipContent side="bottom">
|
|
1192
1277
|
<p className="cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium">
|
|
1193
|
-
<span>Add
|
|
1278
|
+
<span>Add attachments</span>
|
|
1194
1279
|
<code className="cpk:rounded cpk:bg-[#4a4a4a] cpk:px-1 cpk:py-[1px] cpk:font-mono cpk:text-[11px] cpk:text-white cpk:dark:bg-[#e0e0e0] cpk:dark:text-black">
|
|
1195
1280
|
/
|
|
1196
1281
|
</code>
|
|
@@ -1222,25 +1307,9 @@ export namespace CopilotChatInput {
|
|
|
1222
1307
|
() => internalTextareaRef.current as HTMLTextAreaElement,
|
|
1223
1308
|
);
|
|
1224
1309
|
|
|
1225
|
-
// Auto-scroll input into view on mobile when focused
|
|
1226
|
-
useEffect(() => {
|
|
1227
|
-
const textarea = internalTextareaRef.current;
|
|
1228
|
-
if (!textarea) return;
|
|
1229
|
-
|
|
1230
|
-
const handleFocus = () => {
|
|
1231
|
-
// Small delay to let the keyboard start appearing
|
|
1232
|
-
setTimeout(() => {
|
|
1233
|
-
textarea.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
1234
|
-
}, 300);
|
|
1235
|
-
};
|
|
1236
|
-
|
|
1237
|
-
textarea.addEventListener("focus", handleFocus);
|
|
1238
|
-
return () => textarea.removeEventListener("focus", handleFocus);
|
|
1239
|
-
}, []);
|
|
1240
|
-
|
|
1241
1310
|
useEffect(() => {
|
|
1242
1311
|
if (autoFocus) {
|
|
1243
|
-
internalTextareaRef.current?.focus();
|
|
1312
|
+
internalTextareaRef.current?.focus({ preventScroll: true });
|
|
1244
1313
|
}
|
|
1245
1314
|
}, [autoFocus]);
|
|
1246
1315
|
|