@copilotkit/react-core 1.54.1 → 1.55.0-next.8
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 +127 -116
- package/dist/copilotkit-B3Mb1yVE.cjs +7975 -0
- package/dist/copilotkit-B3Mb1yVE.cjs.map +1 -0
- package/dist/copilotkit-DBzgOMby.d.cts +2182 -0
- package/dist/copilotkit-DBzgOMby.d.cts.map +1 -0
- package/dist/copilotkit-DNYSFuz5.mjs +7562 -0
- package/dist/copilotkit-DNYSFuz5.mjs.map +1 -0
- package/dist/copilotkit-Dy5w3qEV.d.mts +2182 -0
- package/dist/copilotkit-Dy5w3qEV.d.mts.map +1 -0
- package/dist/index.cjs +27 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -5
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1941 -35
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +77 -7
- package/dist/v2/index.css +1 -2
- package/dist/v2/index.d.cts +6 -4
- package/dist/v2/index.d.mts +6 -4
- package/dist/v2/index.mjs +7 -4
- package/dist/v2/index.umd.js +5725 -24
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +37 -9
- package/scripts/scope-preflight.mjs +101 -0
- package/src/components/CopilotListeners.tsx +2 -6
- package/src/components/copilot-provider/copilot-messages.tsx +1 -1
- package/src/components/copilot-provider/copilotkit-props.tsx +1 -1
- package/src/components/copilot-provider/copilotkit.tsx +4 -4
- package/src/context/copilot-messages-context.tsx +1 -1
- package/src/hooks/__tests__/use-coagent-config.test.ts +2 -2
- package/src/hooks/__tests__/use-coagent-state-render.e2e.test.tsx +2 -2
- package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +3 -7
- package/src/hooks/__tests__/use-frontend-tool-available.test.tsx +1 -1
- package/src/hooks/__tests__/use-frontend-tool-remount.e2e.test.tsx +4 -4
- package/src/hooks/use-agent-nodename.ts +1 -1
- package/src/hooks/use-coagent-state-render-bridge.tsx +1 -4
- package/src/hooks/use-coagent.ts +1 -1
- package/src/hooks/use-configure-chat-suggestions.tsx +2 -2
- package/src/hooks/use-copilot-chat-suggestions.tsx +2 -2
- package/src/hooks/use-copilot-chat_internal.ts +2 -2
- package/src/hooks/use-copilot-readable.ts +1 -1
- package/src/hooks/use-frontend-tool.ts +2 -2
- package/src/hooks/use-human-in-the-loop.ts +2 -2
- package/src/hooks/use-langgraph-interrupt.ts +2 -5
- package/src/hooks/use-lazy-tool-renderer.tsx +1 -1
- package/src/hooks/use-render-tool-call.ts +1 -1
- package/src/lib/copilot-task.ts +1 -1
- package/src/setupTests.ts +18 -14
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +176 -0
- package/src/v2/__tests__/globalSetup.ts +14 -0
- package/src/v2/__tests__/setup.ts +93 -0
- package/src/v2/__tests__/utils/test-helpers.tsx +470 -0
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +206 -0
- package/src/v2/components/CopilotKitInspector.tsx +50 -0
- package/src/v2/components/MCPAppsActivityRenderer.tsx +785 -0
- package/src/v2/components/WildcardToolCallRender.tsx +86 -0
- package/src/v2/components/__tests__/license-warning-banner.test.tsx +46 -0
- package/src/v2/components/chat/CopilotChat.tsx +431 -0
- package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +375 -0
- package/src/v2/components/chat/CopilotChatAudioRecorder.tsx +350 -0
- package/src/v2/components/chat/CopilotChatInput.tsx +1302 -0
- package/src/v2/components/chat/CopilotChatMessageView.tsx +556 -0
- package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +252 -0
- package/src/v2/components/chat/CopilotChatSuggestionPill.tsx +59 -0
- package/src/v2/components/chat/CopilotChatSuggestionView.tsx +133 -0
- package/src/v2/components/chat/CopilotChatToggleButton.tsx +171 -0
- package/src/v2/components/chat/CopilotChatToolCallsView.tsx +40 -0
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +388 -0
- package/src/v2/components/chat/CopilotChatView.tsx +598 -0
- package/src/v2/components/chat/CopilotModalHeader.tsx +129 -0
- package/src/v2/components/chat/CopilotPopup.tsx +81 -0
- package/src/v2/components/chat/CopilotPopupView.tsx +317 -0
- package/src/v2/components/chat/CopilotSidebar.tsx +76 -0
- package/src/v2/components/chat/CopilotSidebarView.tsx +255 -0
- package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +1113 -0
- package/src/v2/components/chat/__tests__/CopilotChat.onError.test.tsx +73 -0
- package/src/v2/components/chat/__tests__/CopilotChat.slots.e2e.test.tsx +432 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +150 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.tsx +624 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +702 -0
- package/src/v2/components/chat/__tests__/CopilotChatCssClasses.test.tsx +107 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.tsx +929 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +986 -0
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.slots.e2e.test.tsx +1004 -0
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.tsx +169 -0
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.tsx +530 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +782 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +2413 -0
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.slots.e2e.test.tsx +621 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.onClick.e2e.test.tsx +853 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.slots.e2e.test.tsx +1050 -0
- package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.tsx +484 -0
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.tsx +612 -0
- package/src/v2/components/chat/__tests__/CopilotSidebarView.slots.e2e.test.tsx +502 -0
- package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +1011 -0
- package/src/v2/components/chat/__tests__/setup.ts +1 -0
- package/src/v2/components/chat/index.ts +79 -0
- package/src/v2/components/index.ts +7 -0
- package/src/v2/components/license-warning-banner.tsx +198 -0
- package/src/v2/components/ui/button.tsx +123 -0
- package/src/v2/components/ui/dropdown-menu.tsx +258 -0
- package/src/v2/components/ui/tooltip.tsx +60 -0
- package/src/v2/hooks/__tests__/standard-schema-types.test.tsx +152 -0
- package/src/v2/hooks/__tests__/standard-schema.test.tsx +282 -0
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +132 -0
- package/src/v2/hooks/__tests__/use-agent-context.test.tsx +401 -0
- package/src/v2/hooks/__tests__/use-agent-error-state.test.tsx +44 -0
- package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +205 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +148 -0
- package/src/v2/hooks/__tests__/use-component.test.tsx +123 -0
- package/src/v2/hooks/__tests__/use-configure-suggestions.e2e.test.tsx +696 -0
- package/src/v2/hooks/__tests__/use-default-render-tool.test.tsx +153 -0
- package/src/v2/hooks/__tests__/use-frontend-tool-available.test.tsx +167 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +2129 -0
- package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.tsx +1261 -0
- package/src/v2/hooks/__tests__/use-interrupt.test.tsx +397 -0
- package/src/v2/hooks/__tests__/use-katex-styles.test.tsx +56 -0
- package/src/v2/hooks/__tests__/use-keyboard-height.test.tsx +192 -0
- package/src/v2/hooks/__tests__/use-render-tool.test.tsx +259 -0
- package/src/v2/hooks/__tests__/use-suggestions.e2e.test.tsx +524 -0
- package/src/v2/hooks/__tests__/use-threads.test.tsx +433 -0
- package/src/v2/hooks/__tests__/zod-regression.test.tsx +311 -0
- package/src/v2/hooks/index.ts +18 -0
- package/src/v2/hooks/use-agent-context.tsx +45 -0
- package/src/v2/hooks/use-agent.tsx +155 -0
- package/src/v2/hooks/use-component.tsx +89 -0
- package/src/v2/hooks/use-configure-suggestions.tsx +187 -0
- package/src/v2/hooks/use-default-render-tool.tsx +254 -0
- package/src/v2/hooks/use-frontend-tool.tsx +43 -0
- package/src/v2/hooks/use-human-in-the-loop.tsx +81 -0
- package/src/v2/hooks/use-interrupt.tsx +305 -0
- package/src/v2/hooks/use-keyboard-height.tsx +67 -0
- package/src/v2/hooks/use-render-activity-message.tsx +73 -0
- package/src/v2/hooks/use-render-custom-messages.tsx +93 -0
- package/src/v2/hooks/use-render-tool-call.tsx +175 -0
- package/src/v2/hooks/use-render-tool.tsx +181 -0
- package/src/v2/hooks/use-suggestions.tsx +91 -0
- package/src/v2/hooks/use-threads.tsx +256 -0
- package/src/v2/hooks/useKatexStyles.ts +27 -0
- package/src/v2/index.css +1 -1
- package/src/v2/index.ts +18 -2
- package/src/v2/lib/__tests__/completePartialMarkdown.test.ts +495 -0
- package/src/v2/lib/__tests__/renderSlot.test.tsx +588 -0
- package/src/v2/lib/react-core.ts +156 -0
- package/src/v2/lib/slots.tsx +143 -0
- package/src/v2/lib/transcription-client.ts +184 -0
- package/src/v2/lib/utils.ts +8 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +162 -0
- package/src/v2/providers/CopilotKitProvider.tsx +600 -0
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +546 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.license.test.tsx +101 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.onError.test.tsx +69 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.tsx +881 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.stability.test.tsx +740 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +642 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +294 -0
- package/src/v2/providers/index.ts +14 -0
- package/src/v2/styles/globals.css +230 -0
- package/src/v2/types/__tests__/defineToolCallRenderer.test.tsx +525 -0
- package/src/v2/types/defineToolCallRenderer.ts +65 -0
- package/src/v2/types/frontend-tool.ts +8 -0
- package/src/v2/types/human-in-the-loop.ts +33 -0
- package/src/v2/types/index.ts +7 -0
- package/src/v2/types/interrupt.ts +15 -0
- package/src/v2/types/react-activity-message-renderer.ts +27 -0
- package/src/v2/types/react-custom-message-renderer.ts +17 -0
- package/src/v2/types/react-tool-call-renderer.ts +32 -0
- package/tsdown.config.ts +34 -10
- package/vitest.config.mjs +4 -3
- package/LICENSE +0 -21
- package/dist/copilotkit-BRPQ2sqS.d.cts +0 -670
- package/dist/copilotkit-BRPQ2sqS.d.cts.map +0 -1
- package/dist/copilotkit-C94ayZbs.cjs +0 -2161
- package/dist/copilotkit-C94ayZbs.cjs.map +0 -1
- package/dist/copilotkit-CwZMFmSK.d.mts +0 -670
- package/dist/copilotkit-CwZMFmSK.d.mts.map +0 -1
- package/dist/copilotkit-Yh_Ld_FX.mjs +0 -2031
- package/dist/copilotkit-Yh_Ld_FX.mjs.map +0 -1
- package/dist/v2/index.css.map +0 -1
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import React, { useRef, useState, useEffect } from "react";
|
|
2
|
+
import { WithSlots, SlotValue, renderSlot } from "../../lib/slots";
|
|
3
|
+
import CopilotChatMessageView from "./CopilotChatMessageView";
|
|
4
|
+
import CopilotChatInput, {
|
|
5
|
+
CopilotChatInputProps,
|
|
6
|
+
CopilotChatInputMode,
|
|
7
|
+
} from "./CopilotChatInput";
|
|
8
|
+
import CopilotChatSuggestionView, {
|
|
9
|
+
CopilotChatSuggestionViewProps,
|
|
10
|
+
} from "./CopilotChatSuggestionView";
|
|
11
|
+
import { Suggestion } from "@copilotkit/core";
|
|
12
|
+
import { Message } from "@ag-ui/core";
|
|
13
|
+
import { twMerge } from "tailwind-merge";
|
|
14
|
+
import {
|
|
15
|
+
StickToBottom,
|
|
16
|
+
useStickToBottom,
|
|
17
|
+
useStickToBottomContext,
|
|
18
|
+
} from "use-stick-to-bottom";
|
|
19
|
+
import { ChevronDown } from "lucide-react";
|
|
20
|
+
import { Button } from "../../components/ui/button";
|
|
21
|
+
import { cn } from "../../lib/utils";
|
|
22
|
+
import {
|
|
23
|
+
useCopilotChatConfiguration,
|
|
24
|
+
CopilotChatDefaultLabels,
|
|
25
|
+
} from "../../providers/CopilotChatConfigurationProvider";
|
|
26
|
+
import { useKeyboardHeight } from "../../hooks/use-keyboard-height";
|
|
27
|
+
|
|
28
|
+
// Height of the feather gradient overlay (h-24 = 6rem = 96px)
|
|
29
|
+
const FEATHER_HEIGHT = 96;
|
|
30
|
+
|
|
31
|
+
// Forward declaration for WelcomeScreen component type
|
|
32
|
+
export type WelcomeScreenProps = WithSlots<
|
|
33
|
+
{
|
|
34
|
+
welcomeMessage: React.FC<React.HTMLAttributes<HTMLDivElement>>;
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
input: React.ReactElement;
|
|
38
|
+
suggestionView: React.ReactElement;
|
|
39
|
+
} & React.HTMLAttributes<HTMLDivElement>
|
|
40
|
+
>;
|
|
41
|
+
|
|
42
|
+
export type CopilotChatViewProps = WithSlots<
|
|
43
|
+
{
|
|
44
|
+
messageView: typeof CopilotChatMessageView;
|
|
45
|
+
scrollView: typeof CopilotChatView.ScrollView;
|
|
46
|
+
input: typeof CopilotChatInput;
|
|
47
|
+
suggestionView: typeof CopilotChatSuggestionView;
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
messages?: Message[];
|
|
51
|
+
autoScroll?: boolean;
|
|
52
|
+
isRunning?: boolean;
|
|
53
|
+
suggestions?: Suggestion[];
|
|
54
|
+
suggestionLoadingIndexes?: ReadonlyArray<number>;
|
|
55
|
+
onSelectSuggestion?: (suggestion: Suggestion, index: number) => void;
|
|
56
|
+
welcomeScreen?: SlotValue<React.FC<WelcomeScreenProps>> | boolean;
|
|
57
|
+
// Input behavior props
|
|
58
|
+
onSubmitMessage?: (value: string) => void;
|
|
59
|
+
onStop?: () => void;
|
|
60
|
+
inputMode?: CopilotChatInputMode;
|
|
61
|
+
inputValue?: string;
|
|
62
|
+
onInputChange?: (value: string) => void;
|
|
63
|
+
onStartTranscribe?: () => void;
|
|
64
|
+
onCancelTranscribe?: () => void;
|
|
65
|
+
onFinishTranscribe?: () => void;
|
|
66
|
+
onFinishTranscribeWithAudio?: (audioBlob: Blob) => Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated Use the `input` slot's `disclaimer` prop instead:
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <CopilotChat input={{ disclaimer: MyDisclaimer }} />
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
disclaimer?: SlotValue<React.FC<React.HTMLAttributes<HTMLDivElement>>>;
|
|
74
|
+
} & React.HTMLAttributes<HTMLDivElement>
|
|
75
|
+
>;
|
|
76
|
+
|
|
77
|
+
export function CopilotChatView({
|
|
78
|
+
messageView,
|
|
79
|
+
input,
|
|
80
|
+
scrollView,
|
|
81
|
+
suggestionView,
|
|
82
|
+
welcomeScreen,
|
|
83
|
+
messages = [],
|
|
84
|
+
autoScroll = true,
|
|
85
|
+
isRunning = false,
|
|
86
|
+
suggestions,
|
|
87
|
+
suggestionLoadingIndexes,
|
|
88
|
+
onSelectSuggestion,
|
|
89
|
+
// Input behavior props
|
|
90
|
+
onSubmitMessage,
|
|
91
|
+
onStop,
|
|
92
|
+
inputMode,
|
|
93
|
+
inputValue,
|
|
94
|
+
onInputChange,
|
|
95
|
+
onStartTranscribe,
|
|
96
|
+
onCancelTranscribe,
|
|
97
|
+
onFinishTranscribe,
|
|
98
|
+
onFinishTranscribeWithAudio,
|
|
99
|
+
// Deprecated — forwarded to input slot
|
|
100
|
+
disclaimer,
|
|
101
|
+
children,
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: CopilotChatViewProps) {
|
|
105
|
+
const inputContainerRef = useRef<HTMLDivElement>(null);
|
|
106
|
+
const [inputContainerHeight, setInputContainerHeight] = useState(0);
|
|
107
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
108
|
+
const resizeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
109
|
+
|
|
110
|
+
// Track keyboard state for mobile
|
|
111
|
+
const { isKeyboardOpen, keyboardHeight, availableHeight } =
|
|
112
|
+
useKeyboardHeight();
|
|
113
|
+
|
|
114
|
+
// Track input container height changes
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
const element = inputContainerRef.current;
|
|
117
|
+
if (!element) return;
|
|
118
|
+
|
|
119
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
const newHeight = entry.contentRect.height;
|
|
122
|
+
|
|
123
|
+
// Update height and set resizing state
|
|
124
|
+
setInputContainerHeight((prevHeight) => {
|
|
125
|
+
if (newHeight !== prevHeight) {
|
|
126
|
+
setIsResizing(true);
|
|
127
|
+
|
|
128
|
+
// Clear existing timeout
|
|
129
|
+
if (resizeTimeoutRef.current) {
|
|
130
|
+
clearTimeout(resizeTimeoutRef.current);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Set isResizing to false after a short delay
|
|
134
|
+
resizeTimeoutRef.current = setTimeout(() => {
|
|
135
|
+
setIsResizing(false);
|
|
136
|
+
}, 250);
|
|
137
|
+
|
|
138
|
+
return newHeight;
|
|
139
|
+
}
|
|
140
|
+
return prevHeight;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
resizeObserver.observe(element);
|
|
146
|
+
|
|
147
|
+
// Set initial height
|
|
148
|
+
setInputContainerHeight(element.offsetHeight);
|
|
149
|
+
|
|
150
|
+
return () => {
|
|
151
|
+
resizeObserver.disconnect();
|
|
152
|
+
if (resizeTimeoutRef.current) {
|
|
153
|
+
clearTimeout(resizeTimeoutRef.current);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, {
|
|
159
|
+
messages,
|
|
160
|
+
isRunning,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const BoundInput = renderSlot(input, CopilotChatInput, {
|
|
164
|
+
onSubmitMessage,
|
|
165
|
+
onStop,
|
|
166
|
+
mode: inputMode,
|
|
167
|
+
value: inputValue,
|
|
168
|
+
onChange: onInputChange,
|
|
169
|
+
isRunning,
|
|
170
|
+
onStartTranscribe,
|
|
171
|
+
onCancelTranscribe,
|
|
172
|
+
onFinishTranscribe,
|
|
173
|
+
onFinishTranscribeWithAudio,
|
|
174
|
+
positioning: "static",
|
|
175
|
+
keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
|
|
176
|
+
containerRef: inputContainerRef,
|
|
177
|
+
showDisclaimer: true,
|
|
178
|
+
...(disclaimer !== undefined ? { disclaimer } : {}),
|
|
179
|
+
} as CopilotChatInputProps);
|
|
180
|
+
|
|
181
|
+
const hasSuggestions = Array.isArray(suggestions) && suggestions.length > 0;
|
|
182
|
+
const BoundSuggestionView = hasSuggestions
|
|
183
|
+
? renderSlot(suggestionView, CopilotChatSuggestionView, {
|
|
184
|
+
suggestions,
|
|
185
|
+
loadingIndexes: suggestionLoadingIndexes,
|
|
186
|
+
onSelectSuggestion,
|
|
187
|
+
className: "cpk:mb-3 cpk:lg:ml-4 cpk:lg:mr-4 cpk:ml-0 cpk:mr-0",
|
|
188
|
+
})
|
|
189
|
+
: null;
|
|
190
|
+
|
|
191
|
+
const BoundScrollView = renderSlot(scrollView, CopilotChatView.ScrollView, {
|
|
192
|
+
autoScroll,
|
|
193
|
+
inputContainerHeight,
|
|
194
|
+
isResizing,
|
|
195
|
+
children: (
|
|
196
|
+
<div
|
|
197
|
+
style={{
|
|
198
|
+
paddingBottom: `${hasSuggestions ? 4 : 32}px`,
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
<div className="cpk:max-w-3xl cpk:mx-auto">
|
|
202
|
+
{BoundMessageView}
|
|
203
|
+
{hasSuggestions ? (
|
|
204
|
+
<div className="cpk:pl-0 cpk:pr-4 cpk:sm:px-0 cpk:mt-4">
|
|
205
|
+
{BoundSuggestionView}
|
|
206
|
+
</div>
|
|
207
|
+
) : null}
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Welcome screen logic
|
|
214
|
+
const isEmpty = messages.length === 0;
|
|
215
|
+
// Type assertion needed because TypeScript doesn't fully propagate `| boolean` through WithSlots
|
|
216
|
+
const welcomeScreenDisabled = (welcomeScreen as unknown) === false;
|
|
217
|
+
const shouldShowWelcomeScreen = isEmpty && !welcomeScreenDisabled;
|
|
218
|
+
|
|
219
|
+
if (shouldShowWelcomeScreen) {
|
|
220
|
+
// Create a separate input for welcome screen with static positioning and disclaimer visible
|
|
221
|
+
const BoundInputForWelcome = renderSlot(input, CopilotChatInput, {
|
|
222
|
+
onSubmitMessage,
|
|
223
|
+
onStop,
|
|
224
|
+
mode: inputMode,
|
|
225
|
+
value: inputValue,
|
|
226
|
+
onChange: onInputChange,
|
|
227
|
+
isRunning,
|
|
228
|
+
onStartTranscribe,
|
|
229
|
+
onCancelTranscribe,
|
|
230
|
+
onFinishTranscribe,
|
|
231
|
+
onFinishTranscribeWithAudio,
|
|
232
|
+
positioning: "static",
|
|
233
|
+
showDisclaimer: true,
|
|
234
|
+
...(disclaimer !== undefined ? { disclaimer } : {}),
|
|
235
|
+
} as CopilotChatInputProps);
|
|
236
|
+
|
|
237
|
+
// Convert boolean `true` to undefined (use default), and exclude `false` since we've checked for it
|
|
238
|
+
const welcomeScreenSlot = (
|
|
239
|
+
welcomeScreen === true ? undefined : welcomeScreen
|
|
240
|
+
) as SlotValue<React.FC<WelcomeScreenProps>> | undefined;
|
|
241
|
+
const BoundWelcomeScreen = renderSlot(
|
|
242
|
+
welcomeScreenSlot,
|
|
243
|
+
CopilotChatView.WelcomeScreen,
|
|
244
|
+
{
|
|
245
|
+
input: BoundInputForWelcome,
|
|
246
|
+
suggestionView: BoundSuggestionView ?? <></>,
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<div
|
|
252
|
+
data-copilotkit
|
|
253
|
+
data-testid="copilot-chat"
|
|
254
|
+
data-copilot-running={isRunning ? "true" : "false"}
|
|
255
|
+
className={twMerge(
|
|
256
|
+
"copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col",
|
|
257
|
+
className,
|
|
258
|
+
)}
|
|
259
|
+
{...props}
|
|
260
|
+
>
|
|
261
|
+
{BoundWelcomeScreen}
|
|
262
|
+
</div>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (children) {
|
|
267
|
+
return (
|
|
268
|
+
<div data-copilotkit style={{ display: "contents" }}>
|
|
269
|
+
{children({
|
|
270
|
+
messageView: BoundMessageView,
|
|
271
|
+
input: BoundInput,
|
|
272
|
+
scrollView: BoundScrollView,
|
|
273
|
+
suggestionView: BoundSuggestionView ?? <></>,
|
|
274
|
+
})}
|
|
275
|
+
</div>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<div
|
|
281
|
+
data-copilotkit
|
|
282
|
+
data-testid="copilot-chat"
|
|
283
|
+
data-copilot-running={isRunning ? "true" : "false"}
|
|
284
|
+
className={twMerge(
|
|
285
|
+
"copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col",
|
|
286
|
+
className,
|
|
287
|
+
)}
|
|
288
|
+
{...props}
|
|
289
|
+
>
|
|
290
|
+
{BoundScrollView}
|
|
291
|
+
|
|
292
|
+
{BoundInput}
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export namespace CopilotChatView {
|
|
298
|
+
// Inner component that has access to StickToBottom context
|
|
299
|
+
const ScrollContent: React.FC<{
|
|
300
|
+
children: React.ReactNode;
|
|
301
|
+
scrollToBottomButton?: SlotValue<
|
|
302
|
+
React.FC<React.ButtonHTMLAttributes<HTMLButtonElement>>
|
|
303
|
+
>;
|
|
304
|
+
feather?: SlotValue<React.FC<React.HTMLAttributes<HTMLDivElement>>>;
|
|
305
|
+
inputContainerHeight: number;
|
|
306
|
+
isResizing: boolean;
|
|
307
|
+
}> = ({
|
|
308
|
+
children,
|
|
309
|
+
scrollToBottomButton,
|
|
310
|
+
feather,
|
|
311
|
+
inputContainerHeight,
|
|
312
|
+
isResizing,
|
|
313
|
+
}) => {
|
|
314
|
+
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
315
|
+
|
|
316
|
+
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<>
|
|
320
|
+
<StickToBottom.Content
|
|
321
|
+
className="cpk:overflow-y-auto cpk:overflow-x-hidden"
|
|
322
|
+
style={{ flex: "1 1 0%", minHeight: 0 }}
|
|
323
|
+
>
|
|
324
|
+
<div className="cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6">
|
|
325
|
+
{children}
|
|
326
|
+
</div>
|
|
327
|
+
</StickToBottom.Content>
|
|
328
|
+
|
|
329
|
+
{/* Feather gradient overlay */}
|
|
330
|
+
{BoundFeather}
|
|
331
|
+
|
|
332
|
+
{/* Scroll to bottom button - hidden during resize */}
|
|
333
|
+
{!isAtBottom && !isResizing && (
|
|
334
|
+
<div
|
|
335
|
+
className="cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none"
|
|
336
|
+
style={{
|
|
337
|
+
bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px`,
|
|
338
|
+
}}
|
|
339
|
+
>
|
|
340
|
+
{renderSlot(
|
|
341
|
+
scrollToBottomButton,
|
|
342
|
+
CopilotChatView.ScrollToBottomButton,
|
|
343
|
+
{
|
|
344
|
+
onClick: () => scrollToBottom(),
|
|
345
|
+
},
|
|
346
|
+
)}
|
|
347
|
+
</div>
|
|
348
|
+
)}
|
|
349
|
+
</>
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
export const ScrollView: React.FC<
|
|
354
|
+
React.HTMLAttributes<HTMLDivElement> & {
|
|
355
|
+
autoScroll?: boolean;
|
|
356
|
+
scrollToBottomButton?: SlotValue<
|
|
357
|
+
React.FC<React.ButtonHTMLAttributes<HTMLButtonElement>>
|
|
358
|
+
>;
|
|
359
|
+
feather?: SlotValue<React.FC<React.HTMLAttributes<HTMLDivElement>>>;
|
|
360
|
+
inputContainerHeight?: number;
|
|
361
|
+
isResizing?: boolean;
|
|
362
|
+
}
|
|
363
|
+
> = ({
|
|
364
|
+
children,
|
|
365
|
+
autoScroll = true,
|
|
366
|
+
scrollToBottomButton,
|
|
367
|
+
feather,
|
|
368
|
+
inputContainerHeight = 0,
|
|
369
|
+
isResizing = false,
|
|
370
|
+
className,
|
|
371
|
+
...props
|
|
372
|
+
}) => {
|
|
373
|
+
const [hasMounted, setHasMounted] = useState(false);
|
|
374
|
+
const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
|
|
375
|
+
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
376
|
+
|
|
377
|
+
useEffect(() => {
|
|
378
|
+
setHasMounted(true);
|
|
379
|
+
}, []);
|
|
380
|
+
|
|
381
|
+
// Monitor scroll position for non-autoscroll mode
|
|
382
|
+
useEffect(() => {
|
|
383
|
+
if (autoScroll) return; // Skip for autoscroll mode
|
|
384
|
+
|
|
385
|
+
const scrollElement = scrollRef.current;
|
|
386
|
+
if (!scrollElement) return;
|
|
387
|
+
|
|
388
|
+
const checkScroll = () => {
|
|
389
|
+
const atBottom =
|
|
390
|
+
scrollElement.scrollHeight -
|
|
391
|
+
scrollElement.scrollTop -
|
|
392
|
+
scrollElement.clientHeight <
|
|
393
|
+
10;
|
|
394
|
+
setShowScrollButton(!atBottom);
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
checkScroll();
|
|
398
|
+
scrollElement.addEventListener("scroll", checkScroll);
|
|
399
|
+
|
|
400
|
+
// Also check on resize
|
|
401
|
+
const resizeObserver = new ResizeObserver(checkScroll);
|
|
402
|
+
resizeObserver.observe(scrollElement);
|
|
403
|
+
|
|
404
|
+
return () => {
|
|
405
|
+
scrollElement.removeEventListener("scroll", checkScroll);
|
|
406
|
+
resizeObserver.disconnect();
|
|
407
|
+
};
|
|
408
|
+
}, [scrollRef, autoScroll]);
|
|
409
|
+
|
|
410
|
+
if (!hasMounted) {
|
|
411
|
+
return (
|
|
412
|
+
<div className="cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden">
|
|
413
|
+
<div className="cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6">
|
|
414
|
+
{children}
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// When autoScroll is false, we don't use StickToBottom
|
|
421
|
+
if (!autoScroll) {
|
|
422
|
+
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
423
|
+
|
|
424
|
+
return (
|
|
425
|
+
<div
|
|
426
|
+
ref={scrollRef}
|
|
427
|
+
className={cn(
|
|
428
|
+
"cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden cpk:relative",
|
|
429
|
+
className,
|
|
430
|
+
)}
|
|
431
|
+
{...props}
|
|
432
|
+
>
|
|
433
|
+
<div
|
|
434
|
+
ref={contentRef}
|
|
435
|
+
className="cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6"
|
|
436
|
+
>
|
|
437
|
+
{children}
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
{/* Feather gradient overlay */}
|
|
441
|
+
{BoundFeather}
|
|
442
|
+
|
|
443
|
+
{/* Scroll to bottom button for manual mode */}
|
|
444
|
+
{showScrollButton && !isResizing && (
|
|
445
|
+
<div
|
|
446
|
+
className="cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none"
|
|
447
|
+
style={{
|
|
448
|
+
bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px`,
|
|
449
|
+
}}
|
|
450
|
+
>
|
|
451
|
+
{renderSlot(
|
|
452
|
+
scrollToBottomButton,
|
|
453
|
+
CopilotChatView.ScrollToBottomButton,
|
|
454
|
+
{
|
|
455
|
+
onClick: () => scrollToBottom(),
|
|
456
|
+
},
|
|
457
|
+
)}
|
|
458
|
+
</div>
|
|
459
|
+
)}
|
|
460
|
+
</div>
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return (
|
|
465
|
+
<StickToBottom
|
|
466
|
+
className={cn(
|
|
467
|
+
"cpk:flex-1 cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0",
|
|
468
|
+
className,
|
|
469
|
+
)}
|
|
470
|
+
resize="smooth"
|
|
471
|
+
initial="smooth"
|
|
472
|
+
{...props}
|
|
473
|
+
>
|
|
474
|
+
<ScrollContent
|
|
475
|
+
scrollToBottomButton={scrollToBottomButton}
|
|
476
|
+
feather={feather}
|
|
477
|
+
inputContainerHeight={inputContainerHeight}
|
|
478
|
+
isResizing={isResizing}
|
|
479
|
+
>
|
|
480
|
+
{children}
|
|
481
|
+
</ScrollContent>
|
|
482
|
+
</StickToBottom>
|
|
483
|
+
);
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
export const ScrollToBottomButton: React.FC<
|
|
487
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
488
|
+
> = ({ className, ...props }) => (
|
|
489
|
+
<Button
|
|
490
|
+
data-testid="copilot-scroll-to-bottom"
|
|
491
|
+
variant="outline"
|
|
492
|
+
size="sm"
|
|
493
|
+
className={twMerge(
|
|
494
|
+
"cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto",
|
|
495
|
+
"cpk:bg-white cpk:dark:bg-gray-900",
|
|
496
|
+
"cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700",
|
|
497
|
+
"cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800",
|
|
498
|
+
"cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer",
|
|
499
|
+
className,
|
|
500
|
+
)}
|
|
501
|
+
{...props}
|
|
502
|
+
>
|
|
503
|
+
<ChevronDown className="cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" />
|
|
504
|
+
</Button>
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
export const Feather: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
|
|
508
|
+
className,
|
|
509
|
+
style,
|
|
510
|
+
...props
|
|
511
|
+
}) => (
|
|
512
|
+
<div
|
|
513
|
+
className={cn(
|
|
514
|
+
"cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-4 cpk:h-24 cpk:pointer-events-none cpk:z-10 cpk:bg-gradient-to-t",
|
|
515
|
+
"cpk:from-white cpk:via-white cpk:to-transparent",
|
|
516
|
+
"cpk:dark:from-[rgb(33,33,33)] cpk:dark:via-[rgb(33,33,33)]",
|
|
517
|
+
className,
|
|
518
|
+
)}
|
|
519
|
+
style={style}
|
|
520
|
+
{...props}
|
|
521
|
+
/>
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
export const WelcomeMessage: React.FC<
|
|
525
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
526
|
+
> = ({ className, ...props }) => {
|
|
527
|
+
const config = useCopilotChatConfiguration();
|
|
528
|
+
const labels = config?.labels ?? CopilotChatDefaultLabels;
|
|
529
|
+
|
|
530
|
+
return (
|
|
531
|
+
<h1
|
|
532
|
+
className={cn(
|
|
533
|
+
"cpk:text-xl cpk:sm:text-2xl cpk:font-medium cpk:text-foreground cpk:text-center",
|
|
534
|
+
className,
|
|
535
|
+
)}
|
|
536
|
+
{...props}
|
|
537
|
+
>
|
|
538
|
+
{labels.welcomeMessageText}
|
|
539
|
+
</h1>
|
|
540
|
+
);
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
export const WelcomeScreen: React.FC<WelcomeScreenProps> = ({
|
|
544
|
+
welcomeMessage,
|
|
545
|
+
input,
|
|
546
|
+
suggestionView,
|
|
547
|
+
className,
|
|
548
|
+
children,
|
|
549
|
+
...props
|
|
550
|
+
}) => {
|
|
551
|
+
// Render the welcomeMessage slot internally
|
|
552
|
+
const BoundWelcomeMessage = renderSlot(
|
|
553
|
+
welcomeMessage,
|
|
554
|
+
CopilotChatView.WelcomeMessage,
|
|
555
|
+
{},
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
if (children) {
|
|
559
|
+
return (
|
|
560
|
+
<div data-copilotkit style={{ display: "contents" }}>
|
|
561
|
+
{children({
|
|
562
|
+
welcomeMessage: BoundWelcomeMessage,
|
|
563
|
+
input,
|
|
564
|
+
suggestionView,
|
|
565
|
+
className,
|
|
566
|
+
...props,
|
|
567
|
+
})}
|
|
568
|
+
</div>
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return (
|
|
573
|
+
<div
|
|
574
|
+
data-testid="copilot-welcome-screen"
|
|
575
|
+
className={cn(
|
|
576
|
+
"cpk:flex-1 cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:px-4",
|
|
577
|
+
className,
|
|
578
|
+
)}
|
|
579
|
+
{...props}
|
|
580
|
+
>
|
|
581
|
+
<div className="cpk:w-full cpk:max-w-3xl cpk:flex cpk:flex-col cpk:items-center">
|
|
582
|
+
{/* Welcome message */}
|
|
583
|
+
<div className="cpk:mb-6">{BoundWelcomeMessage}</div>
|
|
584
|
+
|
|
585
|
+
{/* Input */}
|
|
586
|
+
<div className="cpk:w-full">{input}</div>
|
|
587
|
+
|
|
588
|
+
{/* Suggestions */}
|
|
589
|
+
<div className="cpk:mt-4 cpk:flex cpk:justify-center">
|
|
590
|
+
{suggestionView}
|
|
591
|
+
</div>
|
|
592
|
+
</div>
|
|
593
|
+
</div>
|
|
594
|
+
);
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export default CopilotChatView;
|