@copilotkitnext/react 0.0.2 → 0.0.4
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/package.json +5 -5
- package/.turbo/turbo-build$colon$css.log +0 -9
- package/.turbo/turbo-build.log +0 -30
- package/.turbo/turbo-check-types.log +0 -0
- package/.turbo/turbo-lint.log +0 -78
- package/.turbo/turbo-test.log +0 -79
- package/postcss.config.js +0 -7
- package/src/__tests__/setup.ts +0 -2
- package/src/components/chat/CopilotChat.tsx +0 -90
- package/src/components/chat/CopilotChatAssistantMessage.tsx +0 -478
- package/src/components/chat/CopilotChatAudioRecorder.tsx +0 -157
- package/src/components/chat/CopilotChatInput.tsx +0 -596
- package/src/components/chat/CopilotChatMessageView.tsx +0 -85
- package/src/components/chat/CopilotChatToolCallsView.tsx +0 -43
- package/src/components/chat/CopilotChatUserMessage.tsx +0 -337
- package/src/components/chat/CopilotChatView.tsx +0 -385
- package/src/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +0 -684
- package/src/components/chat/__tests__/CopilotChatInput.test.tsx +0 -531
- package/src/components/chat/__tests__/setup.ts +0 -1
- package/src/components/chat/index.ts +0 -35
- package/src/components/index.ts +0 -4
- package/src/components/ui/button.tsx +0 -123
- package/src/components/ui/dropdown-menu.tsx +0 -257
- package/src/components/ui/tooltip.tsx +0 -59
- package/src/hooks/index.ts +0 -6
- package/src/hooks/use-agent-context.tsx +0 -17
- package/src/hooks/use-agent.tsx +0 -48
- package/src/hooks/use-frontend-tool.tsx +0 -46
- package/src/hooks/use-human-in-the-loop.tsx +0 -76
- package/src/hooks/use-render-tool-call.tsx +0 -81
- package/src/index.ts +0 -4
- package/src/lib/__tests__/completePartialMarkdown.test.ts +0 -495
- package/src/lib/__tests__/renderSlot.test.tsx +0 -610
- package/src/lib/slots.tsx +0 -55
- package/src/lib/utils.ts +0 -6
- package/src/providers/CopilotChatConfigurationProvider.tsx +0 -81
- package/src/providers/CopilotKitProvider.tsx +0 -269
- package/src/providers/__tests__/CopilotKitProvider.test.tsx +0 -487
- package/src/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +0 -261
- package/src/providers/index.ts +0 -14
- package/src/styles/globals.css +0 -302
- package/src/types/frontend-tool.ts +0 -8
- package/src/types/human-in-the-loop.ts +0 -33
- package/src/types/index.ts +0 -3
- package/src/types/react-tool-call-render.ts +0 -29
- package/tailwind.config.js +0 -9
- package/tsconfig.json +0 -23
- package/tsup.config.ts +0 -19
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
useState,
|
|
3
|
-
useRef,
|
|
4
|
-
KeyboardEvent,
|
|
5
|
-
ChangeEvent,
|
|
6
|
-
useEffect,
|
|
7
|
-
forwardRef,
|
|
8
|
-
useImperativeHandle,
|
|
9
|
-
} from "react";
|
|
10
|
-
import { twMerge } from "tailwind-merge";
|
|
11
|
-
import { Plus, Settings2, Mic, ArrowUp, X, Check } from "lucide-react";
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
CopilotChatLabels,
|
|
15
|
-
useCopilotChatConfiguration,
|
|
16
|
-
} from "@/providers/CopilotChatConfigurationProvider";
|
|
17
|
-
import { Button } from "@/components/ui/button";
|
|
18
|
-
import {
|
|
19
|
-
Tooltip,
|
|
20
|
-
TooltipContent,
|
|
21
|
-
TooltipTrigger,
|
|
22
|
-
} from "@/components/ui/tooltip";
|
|
23
|
-
import {
|
|
24
|
-
DropdownMenu,
|
|
25
|
-
DropdownMenuTrigger,
|
|
26
|
-
DropdownMenuContent,
|
|
27
|
-
DropdownMenuItem,
|
|
28
|
-
DropdownMenuSub,
|
|
29
|
-
DropdownMenuSubTrigger,
|
|
30
|
-
DropdownMenuSubContent,
|
|
31
|
-
DropdownMenuSeparator,
|
|
32
|
-
} from "@/components/ui/dropdown-menu";
|
|
33
|
-
|
|
34
|
-
import { CopilotChatAudioRecorder } from "./CopilotChatAudioRecorder";
|
|
35
|
-
import { renderSlot, WithSlots } from "@/lib/slots";
|
|
36
|
-
|
|
37
|
-
export type CopilotChatInputMode = "input" | "transcribe" | "processing";
|
|
38
|
-
|
|
39
|
-
export type ToolsMenuItem = {
|
|
40
|
-
label: string;
|
|
41
|
-
} & (
|
|
42
|
-
| {
|
|
43
|
-
action: () => void;
|
|
44
|
-
items?: never;
|
|
45
|
-
}
|
|
46
|
-
| {
|
|
47
|
-
action?: never;
|
|
48
|
-
items: (ToolsMenuItem | "-")[];
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
export type CopilotChatInputProps = WithSlots<
|
|
53
|
-
{
|
|
54
|
-
textArea: typeof CopilotChatInput.TextArea;
|
|
55
|
-
sendButton: typeof CopilotChatInput.SendButton;
|
|
56
|
-
startTranscribeButton: typeof CopilotChatInput.StartTranscribeButton;
|
|
57
|
-
cancelTranscribeButton: typeof CopilotChatInput.CancelTranscribeButton;
|
|
58
|
-
finishTranscribeButton: typeof CopilotChatInput.FinishTranscribeButton;
|
|
59
|
-
addFileButton: typeof CopilotChatInput.AddFileButton;
|
|
60
|
-
toolsButton: typeof CopilotChatInput.ToolsButton;
|
|
61
|
-
toolbar: typeof CopilotChatInput.Toolbar;
|
|
62
|
-
audioRecorder: typeof CopilotChatAudioRecorder;
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
mode?: CopilotChatInputMode;
|
|
66
|
-
toolsMenu?: (ToolsMenuItem | "-")[];
|
|
67
|
-
autoFocus?: boolean;
|
|
68
|
-
additionalToolbarItems?: React.ReactNode;
|
|
69
|
-
onSubmitMessage?: (value: string) => void;
|
|
70
|
-
onStartTranscribe?: () => void;
|
|
71
|
-
onCancelTranscribe?: () => void;
|
|
72
|
-
onFinishTranscribe?: () => void;
|
|
73
|
-
onAddFile?: () => void;
|
|
74
|
-
value?: string;
|
|
75
|
-
onChange?: (value: string) => void;
|
|
76
|
-
} & Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">
|
|
77
|
-
>;
|
|
78
|
-
|
|
79
|
-
export function CopilotChatInput({
|
|
80
|
-
mode = "input",
|
|
81
|
-
onSubmitMessage,
|
|
82
|
-
onStartTranscribe,
|
|
83
|
-
onCancelTranscribe,
|
|
84
|
-
onFinishTranscribe,
|
|
85
|
-
onAddFile,
|
|
86
|
-
onChange,
|
|
87
|
-
value,
|
|
88
|
-
toolsMenu,
|
|
89
|
-
autoFocus = true,
|
|
90
|
-
additionalToolbarItems,
|
|
91
|
-
textArea,
|
|
92
|
-
sendButton,
|
|
93
|
-
startTranscribeButton,
|
|
94
|
-
cancelTranscribeButton,
|
|
95
|
-
finishTranscribeButton,
|
|
96
|
-
addFileButton,
|
|
97
|
-
toolsButton,
|
|
98
|
-
toolbar,
|
|
99
|
-
audioRecorder,
|
|
100
|
-
children,
|
|
101
|
-
className,
|
|
102
|
-
...props
|
|
103
|
-
}: CopilotChatInputProps) {
|
|
104
|
-
const { inputValue, onSubmitInput, onChangeInput } =
|
|
105
|
-
useCopilotChatConfiguration();
|
|
106
|
-
|
|
107
|
-
value ??= inputValue;
|
|
108
|
-
onSubmitMessage ??= onSubmitInput;
|
|
109
|
-
onChange ??= onChangeInput;
|
|
110
|
-
|
|
111
|
-
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
112
|
-
const audioRecorderRef =
|
|
113
|
-
useRef<React.ElementRef<typeof CopilotChatAudioRecorder>>(null);
|
|
114
|
-
|
|
115
|
-
// Handle recording based on mode changes
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
const recorder = audioRecorderRef.current;
|
|
118
|
-
if (!recorder) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (mode === "transcribe") {
|
|
123
|
-
// Start recording when entering transcribe mode
|
|
124
|
-
recorder.start().catch(console.error);
|
|
125
|
-
} else {
|
|
126
|
-
// Stop recording when leaving transcribe mode
|
|
127
|
-
if (recorder.state === "recording") {
|
|
128
|
-
recorder.stop().catch(console.error);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}, [mode]);
|
|
132
|
-
|
|
133
|
-
// Handlers
|
|
134
|
-
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
135
|
-
onChange?.(e.target.value);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
|
139
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
140
|
-
e.preventDefault();
|
|
141
|
-
send();
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const send = () => {
|
|
146
|
-
const trimmed = value?.trim();
|
|
147
|
-
if (trimmed) {
|
|
148
|
-
onSubmitMessage?.(trimmed);
|
|
149
|
-
// Refocus input after sending
|
|
150
|
-
if (inputRef.current) {
|
|
151
|
-
inputRef.current.focus();
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const BoundTextArea = renderSlot(textArea, CopilotChatInput.TextArea, {
|
|
157
|
-
ref: inputRef,
|
|
158
|
-
value,
|
|
159
|
-
onChange: handleChange,
|
|
160
|
-
onKeyDown: handleKeyDown,
|
|
161
|
-
autoFocus: autoFocus,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const BoundAudioRecorder = renderSlot(
|
|
165
|
-
audioRecorder,
|
|
166
|
-
CopilotChatAudioRecorder,
|
|
167
|
-
{
|
|
168
|
-
ref: audioRecorderRef,
|
|
169
|
-
}
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, {
|
|
173
|
-
onClick: send,
|
|
174
|
-
disabled: !value?.trim() || !onSubmitMessage,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
const BoundStartTranscribeButton = renderSlot(
|
|
178
|
-
startTranscribeButton,
|
|
179
|
-
CopilotChatInput.StartTranscribeButton,
|
|
180
|
-
{
|
|
181
|
-
onClick: onStartTranscribe,
|
|
182
|
-
}
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const BoundCancelTranscribeButton = renderSlot(
|
|
186
|
-
cancelTranscribeButton,
|
|
187
|
-
CopilotChatInput.CancelTranscribeButton,
|
|
188
|
-
{
|
|
189
|
-
onClick: onCancelTranscribe,
|
|
190
|
-
}
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const BoundFinishTranscribeButton = renderSlot(
|
|
194
|
-
finishTranscribeButton,
|
|
195
|
-
CopilotChatInput.FinishTranscribeButton,
|
|
196
|
-
{
|
|
197
|
-
onClick: onFinishTranscribe,
|
|
198
|
-
}
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const BoundAddFileButton = renderSlot(
|
|
202
|
-
addFileButton,
|
|
203
|
-
CopilotChatInput.AddFileButton,
|
|
204
|
-
{
|
|
205
|
-
onClick: onAddFile,
|
|
206
|
-
disabled: mode === "transcribe",
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
const BoundToolsButton = renderSlot(
|
|
211
|
-
toolsButton,
|
|
212
|
-
CopilotChatInput.ToolsButton,
|
|
213
|
-
{
|
|
214
|
-
disabled: mode === "transcribe",
|
|
215
|
-
toolsMenu: toolsMenu,
|
|
216
|
-
}
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
const BoundToolbar = renderSlot(
|
|
220
|
-
typeof toolbar === "string" || toolbar === undefined
|
|
221
|
-
? twMerge(
|
|
222
|
-
toolbar,
|
|
223
|
-
"w-full h-[60px] bg-transparent flex items-center justify-between"
|
|
224
|
-
)
|
|
225
|
-
: toolbar,
|
|
226
|
-
CopilotChatInput.Toolbar,
|
|
227
|
-
{
|
|
228
|
-
children: (
|
|
229
|
-
<>
|
|
230
|
-
<div className="flex items-center">
|
|
231
|
-
{onAddFile && BoundAddFileButton}
|
|
232
|
-
{BoundToolsButton}
|
|
233
|
-
{additionalToolbarItems}
|
|
234
|
-
</div>
|
|
235
|
-
<div className="flex items-center">
|
|
236
|
-
{mode === "transcribe" ? (
|
|
237
|
-
<>
|
|
238
|
-
{onCancelTranscribe && BoundCancelTranscribeButton}
|
|
239
|
-
{onFinishTranscribe && BoundFinishTranscribeButton}
|
|
240
|
-
</>
|
|
241
|
-
) : (
|
|
242
|
-
<>
|
|
243
|
-
{onStartTranscribe && BoundStartTranscribeButton}
|
|
244
|
-
{BoundSendButton}
|
|
245
|
-
</>
|
|
246
|
-
)}
|
|
247
|
-
</div>
|
|
248
|
-
</>
|
|
249
|
-
),
|
|
250
|
-
}
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
if (children) {
|
|
254
|
-
return (
|
|
255
|
-
<>
|
|
256
|
-
{children({
|
|
257
|
-
textArea: BoundTextArea,
|
|
258
|
-
audioRecorder: BoundAudioRecorder,
|
|
259
|
-
sendButton: BoundSendButton,
|
|
260
|
-
startTranscribeButton: BoundStartTranscribeButton,
|
|
261
|
-
cancelTranscribeButton: BoundCancelTranscribeButton,
|
|
262
|
-
finishTranscribeButton: BoundFinishTranscribeButton,
|
|
263
|
-
addFileButton: BoundAddFileButton,
|
|
264
|
-
toolsButton: BoundToolsButton,
|
|
265
|
-
toolbar: BoundToolbar,
|
|
266
|
-
onSubmitMessage,
|
|
267
|
-
onStartTranscribe,
|
|
268
|
-
onCancelTranscribe,
|
|
269
|
-
onFinishTranscribe,
|
|
270
|
-
onAddFile,
|
|
271
|
-
mode,
|
|
272
|
-
toolsMenu,
|
|
273
|
-
autoFocus,
|
|
274
|
-
additionalToolbarItems,
|
|
275
|
-
})}
|
|
276
|
-
</>
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return (
|
|
281
|
-
<div
|
|
282
|
-
className={twMerge(
|
|
283
|
-
// Layout
|
|
284
|
-
"flex w-full flex-col items-center justify-center",
|
|
285
|
-
// Interaction
|
|
286
|
-
"cursor-text",
|
|
287
|
-
// Overflow and clipping
|
|
288
|
-
"overflow-visible bg-clip-padding contain-inline-size",
|
|
289
|
-
// Background
|
|
290
|
-
"bg-white dark:bg-[#303030]",
|
|
291
|
-
// Visual effects
|
|
292
|
-
"shadow-[0_4px_4px_0_#0000000a,0_0_1px_0_#0000009e] rounded-[28px]",
|
|
293
|
-
className
|
|
294
|
-
)}
|
|
295
|
-
{...props}
|
|
296
|
-
>
|
|
297
|
-
{mode === "transcribe" ? BoundAudioRecorder : BoundTextArea}
|
|
298
|
-
{BoundToolbar}
|
|
299
|
-
</div>
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
304
|
-
export namespace CopilotChatInput {
|
|
305
|
-
export const SendButton: React.FC<
|
|
306
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
307
|
-
> = ({ className, ...props }) => (
|
|
308
|
-
<div className="mr-[10px]">
|
|
309
|
-
<Button
|
|
310
|
-
type="button"
|
|
311
|
-
variant="chatInputToolbarPrimary"
|
|
312
|
-
size="chatInputToolbarIcon"
|
|
313
|
-
className={className}
|
|
314
|
-
{...props}
|
|
315
|
-
>
|
|
316
|
-
<ArrowUp className="size-[18px]" />
|
|
317
|
-
</Button>
|
|
318
|
-
</div>
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
export const ToolbarButton: React.FC<
|
|
322
|
-
React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
323
|
-
icon: React.ReactNode;
|
|
324
|
-
labelKey: keyof CopilotChatLabels;
|
|
325
|
-
defaultClassName?: string;
|
|
326
|
-
}
|
|
327
|
-
> = ({ icon, labelKey, defaultClassName, className, ...props }) => {
|
|
328
|
-
const { labels } = useCopilotChatConfiguration();
|
|
329
|
-
return (
|
|
330
|
-
<Tooltip>
|
|
331
|
-
<TooltipTrigger asChild>
|
|
332
|
-
<Button
|
|
333
|
-
type="button"
|
|
334
|
-
variant="chatInputToolbarSecondary"
|
|
335
|
-
size="chatInputToolbarIcon"
|
|
336
|
-
className={twMerge(defaultClassName, className)}
|
|
337
|
-
{...props}
|
|
338
|
-
>
|
|
339
|
-
{icon}
|
|
340
|
-
</Button>
|
|
341
|
-
</TooltipTrigger>
|
|
342
|
-
<TooltipContent side="bottom">
|
|
343
|
-
<p>{labels[labelKey]}</p>
|
|
344
|
-
</TooltipContent>
|
|
345
|
-
</Tooltip>
|
|
346
|
-
);
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
export const StartTranscribeButton: React.FC<
|
|
350
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
351
|
-
> = (props) => (
|
|
352
|
-
<ToolbarButton
|
|
353
|
-
icon={<Mic className="size-[18px]" />}
|
|
354
|
-
labelKey="chatInputToolbarStartTranscribeButtonLabel"
|
|
355
|
-
defaultClassName="mr-2"
|
|
356
|
-
{...props}
|
|
357
|
-
/>
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
export const CancelTranscribeButton: React.FC<
|
|
361
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
362
|
-
> = (props) => (
|
|
363
|
-
<ToolbarButton
|
|
364
|
-
icon={<X className="size-[18px]" />}
|
|
365
|
-
labelKey="chatInputToolbarCancelTranscribeButtonLabel"
|
|
366
|
-
defaultClassName="mr-2"
|
|
367
|
-
{...props}
|
|
368
|
-
/>
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
export const FinishTranscribeButton: React.FC<
|
|
372
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
373
|
-
> = (props) => (
|
|
374
|
-
<ToolbarButton
|
|
375
|
-
icon={<Check className="size-[18px]" />}
|
|
376
|
-
labelKey="chatInputToolbarFinishTranscribeButtonLabel"
|
|
377
|
-
defaultClassName="mr-[10px]"
|
|
378
|
-
{...props}
|
|
379
|
-
/>
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
export const AddFileButton: React.FC<
|
|
383
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
384
|
-
> = (props) => (
|
|
385
|
-
<ToolbarButton
|
|
386
|
-
icon={<Plus className="size-[20px]" />}
|
|
387
|
-
labelKey="chatInputToolbarAddButtonLabel"
|
|
388
|
-
defaultClassName="ml-2"
|
|
389
|
-
{...props}
|
|
390
|
-
/>
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
export const ToolsButton: React.FC<
|
|
394
|
-
React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
395
|
-
toolsMenu?: (ToolsMenuItem | "-")[];
|
|
396
|
-
}
|
|
397
|
-
> = ({ className, toolsMenu, ...props }) => {
|
|
398
|
-
const { labels } = useCopilotChatConfiguration();
|
|
399
|
-
|
|
400
|
-
const renderMenuItems = (
|
|
401
|
-
items: (ToolsMenuItem | "-")[]
|
|
402
|
-
): React.ReactNode => {
|
|
403
|
-
return items.map((item, index) => {
|
|
404
|
-
if (item === "-") {
|
|
405
|
-
// Separator
|
|
406
|
-
return <DropdownMenuSeparator key={index} />;
|
|
407
|
-
} else if (item.items && item.items.length > 0) {
|
|
408
|
-
// Nested menu
|
|
409
|
-
return (
|
|
410
|
-
<DropdownMenuSub key={index}>
|
|
411
|
-
<DropdownMenuSubTrigger>{item.label}</DropdownMenuSubTrigger>
|
|
412
|
-
<DropdownMenuSubContent>
|
|
413
|
-
{renderMenuItems(item.items)}
|
|
414
|
-
</DropdownMenuSubContent>
|
|
415
|
-
</DropdownMenuSub>
|
|
416
|
-
);
|
|
417
|
-
} else {
|
|
418
|
-
// Regular menu item
|
|
419
|
-
return (
|
|
420
|
-
<DropdownMenuItem key={index} onClick={item.action}>
|
|
421
|
-
{item.label}
|
|
422
|
-
</DropdownMenuItem>
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
// Only render if toolsMenu is provided and has items
|
|
429
|
-
if (!toolsMenu || toolsMenu.length === 0) {
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Render dropdown menu
|
|
434
|
-
return (
|
|
435
|
-
<DropdownMenu>
|
|
436
|
-
<DropdownMenuTrigger asChild>
|
|
437
|
-
<Button
|
|
438
|
-
type="button"
|
|
439
|
-
variant="chatInputToolbarSecondary"
|
|
440
|
-
size="chatInputToolbarIconLabel"
|
|
441
|
-
className={className}
|
|
442
|
-
{...props}
|
|
443
|
-
>
|
|
444
|
-
<Settings2 className="size-[18px]" />
|
|
445
|
-
<span className="text-sm font-normal">
|
|
446
|
-
{labels.chatInputToolbarToolsButtonLabel}
|
|
447
|
-
</span>
|
|
448
|
-
</Button>
|
|
449
|
-
</DropdownMenuTrigger>
|
|
450
|
-
<DropdownMenuContent side="top" align="end">
|
|
451
|
-
{renderMenuItems(toolsMenu)}
|
|
452
|
-
</DropdownMenuContent>
|
|
453
|
-
</DropdownMenu>
|
|
454
|
-
);
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
export const Toolbar: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
|
|
458
|
-
className,
|
|
459
|
-
...props
|
|
460
|
-
}) => (
|
|
461
|
-
<div
|
|
462
|
-
className={twMerge(
|
|
463
|
-
"w-full h-[60px] bg-transparent flex items-center",
|
|
464
|
-
className
|
|
465
|
-
)}
|
|
466
|
-
{...props}
|
|
467
|
-
/>
|
|
468
|
-
);
|
|
469
|
-
|
|
470
|
-
export interface TextAreaProps
|
|
471
|
-
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
472
|
-
maxRows?: number;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
476
|
-
function TextArea({ maxRows = 5, style, className, ...props }, ref) {
|
|
477
|
-
const internalTextareaRef = useRef<HTMLTextAreaElement>(null);
|
|
478
|
-
const [maxHeight, setMaxHeight] = useState<number>(0);
|
|
479
|
-
|
|
480
|
-
const { labels } = useCopilotChatConfiguration();
|
|
481
|
-
|
|
482
|
-
useImperativeHandle(
|
|
483
|
-
ref,
|
|
484
|
-
() => internalTextareaRef.current as HTMLTextAreaElement
|
|
485
|
-
);
|
|
486
|
-
|
|
487
|
-
const adjustHeight = () => {
|
|
488
|
-
const textarea = internalTextareaRef.current;
|
|
489
|
-
if (textarea && maxHeight > 0) {
|
|
490
|
-
textarea.style.height = "auto";
|
|
491
|
-
textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`;
|
|
492
|
-
}
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
useEffect(() => {
|
|
496
|
-
const calculateMaxHeight = () => {
|
|
497
|
-
const textarea = internalTextareaRef.current;
|
|
498
|
-
if (textarea) {
|
|
499
|
-
// Save current value
|
|
500
|
-
const currentValue = textarea.value;
|
|
501
|
-
// Clear content to measure single row height
|
|
502
|
-
textarea.value = "";
|
|
503
|
-
textarea.style.height = "auto";
|
|
504
|
-
|
|
505
|
-
// Get computed styles to account for padding
|
|
506
|
-
const computedStyle = window.getComputedStyle(textarea);
|
|
507
|
-
const paddingTop = parseFloat(computedStyle.paddingTop);
|
|
508
|
-
const paddingBottom = parseFloat(computedStyle.paddingBottom);
|
|
509
|
-
|
|
510
|
-
// Calculate actual content height (without padding)
|
|
511
|
-
const contentHeight =
|
|
512
|
-
textarea.scrollHeight - paddingTop - paddingBottom;
|
|
513
|
-
|
|
514
|
-
// Calculate max height: content height for maxRows + padding
|
|
515
|
-
setMaxHeight(contentHeight * maxRows + paddingTop + paddingBottom);
|
|
516
|
-
|
|
517
|
-
// Restore original value
|
|
518
|
-
textarea.value = currentValue;
|
|
519
|
-
|
|
520
|
-
// Adjust height after calculating maxHeight
|
|
521
|
-
if (currentValue) {
|
|
522
|
-
textarea.style.height = "auto";
|
|
523
|
-
textarea.style.height = `${Math.min(textarea.scrollHeight, contentHeight * maxRows + paddingTop + paddingBottom)}px`;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (props.autoFocus) {
|
|
527
|
-
textarea.focus();
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
calculateMaxHeight();
|
|
533
|
-
}, [maxRows, props.autoFocus]);
|
|
534
|
-
|
|
535
|
-
// Adjust height when controlled value changes
|
|
536
|
-
useEffect(() => {
|
|
537
|
-
adjustHeight();
|
|
538
|
-
}, [props.value, maxHeight]);
|
|
539
|
-
|
|
540
|
-
// Handle input events for uncontrolled usage
|
|
541
|
-
const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
|
|
542
|
-
adjustHeight();
|
|
543
|
-
// Call the original onChange if provided
|
|
544
|
-
if (props.onChange) {
|
|
545
|
-
props.onChange(e as React.ChangeEvent<HTMLTextAreaElement>);
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
return (
|
|
550
|
-
<textarea
|
|
551
|
-
ref={internalTextareaRef}
|
|
552
|
-
{...props}
|
|
553
|
-
onChange={handleInput}
|
|
554
|
-
style={{
|
|
555
|
-
overflow: "auto",
|
|
556
|
-
resize: "none",
|
|
557
|
-
maxHeight: `${maxHeight}px`,
|
|
558
|
-
...style,
|
|
559
|
-
}}
|
|
560
|
-
placeholder={labels.chatInputPlaceholder}
|
|
561
|
-
className={twMerge(
|
|
562
|
-
// Layout and sizing
|
|
563
|
-
"w-full p-5 pb-0",
|
|
564
|
-
// Behavior
|
|
565
|
-
"outline-none resize-none",
|
|
566
|
-
// Background
|
|
567
|
-
"bg-transparent",
|
|
568
|
-
// Typography
|
|
569
|
-
"antialiased font-regular leading-relaxed text-[16px]",
|
|
570
|
-
// Placeholder styles
|
|
571
|
-
"placeholder:text-[#00000077] dark:placeholder:text-[#fffc]",
|
|
572
|
-
className
|
|
573
|
-
)}
|
|
574
|
-
rows={1}
|
|
575
|
-
/>
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
export const AudioRecorder = CopilotChatAudioRecorder;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
CopilotChatInput.TextArea.displayName = "CopilotChatInput.TextArea";
|
|
584
|
-
CopilotChatInput.SendButton.displayName = "CopilotChatInput.SendButton";
|
|
585
|
-
CopilotChatInput.ToolbarButton.displayName = "CopilotChatInput.ToolbarButton";
|
|
586
|
-
CopilotChatInput.StartTranscribeButton.displayName =
|
|
587
|
-
"CopilotChatInput.StartTranscribeButton";
|
|
588
|
-
CopilotChatInput.CancelTranscribeButton.displayName =
|
|
589
|
-
"CopilotChatInput.CancelTranscribeButton";
|
|
590
|
-
CopilotChatInput.FinishTranscribeButton.displayName =
|
|
591
|
-
"CopilotChatInput.FinishTranscribeButton";
|
|
592
|
-
CopilotChatInput.AddFileButton.displayName = "CopilotChatInput.AddButton";
|
|
593
|
-
CopilotChatInput.ToolsButton.displayName = "CopilotChatInput.ToolsButton";
|
|
594
|
-
CopilotChatInput.Toolbar.displayName = "CopilotChatInput.Toolbar";
|
|
595
|
-
|
|
596
|
-
export default CopilotChatInput;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { WithSlots, renderSlot } from "@/lib/slots";
|
|
2
|
-
import CopilotChatAssistantMessage from "./CopilotChatAssistantMessage";
|
|
3
|
-
import CopilotChatUserMessage from "./CopilotChatUserMessage";
|
|
4
|
-
import { Message } from "@ag-ui/core";
|
|
5
|
-
import { twMerge } from "tailwind-merge";
|
|
6
|
-
|
|
7
|
-
export type CopilotChatMessageViewProps = Omit<
|
|
8
|
-
WithSlots<
|
|
9
|
-
{
|
|
10
|
-
assistantMessage: typeof CopilotChatAssistantMessage;
|
|
11
|
-
userMessage: typeof CopilotChatUserMessage;
|
|
12
|
-
cursor: typeof CopilotChatMessageView.Cursor;
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
isLoading?: boolean;
|
|
16
|
-
messages?: Message[];
|
|
17
|
-
} & React.HTMLAttributes<HTMLDivElement>
|
|
18
|
-
>,
|
|
19
|
-
"children"
|
|
20
|
-
> & {
|
|
21
|
-
children?: (props: {
|
|
22
|
-
isLoading: boolean;
|
|
23
|
-
messages: Message[];
|
|
24
|
-
messageElements: React.ReactElement[];
|
|
25
|
-
}) => React.ReactElement;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export function CopilotChatMessageView({
|
|
29
|
-
messages = [],
|
|
30
|
-
assistantMessage,
|
|
31
|
-
userMessage,
|
|
32
|
-
cursor,
|
|
33
|
-
isLoading = false,
|
|
34
|
-
children,
|
|
35
|
-
className,
|
|
36
|
-
...props
|
|
37
|
-
}: CopilotChatMessageViewProps) {
|
|
38
|
-
const messageElements: React.ReactElement[] = messages
|
|
39
|
-
.map((message) => {
|
|
40
|
-
if (message.role === "assistant") {
|
|
41
|
-
return renderSlot(assistantMessage, CopilotChatAssistantMessage, {
|
|
42
|
-
key: message.id,
|
|
43
|
-
message,
|
|
44
|
-
messages,
|
|
45
|
-
isLoading,
|
|
46
|
-
});
|
|
47
|
-
} else if (message.role === "user") {
|
|
48
|
-
return renderSlot(userMessage, CopilotChatUserMessage, {
|
|
49
|
-
key: message.id,
|
|
50
|
-
message,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return;
|
|
55
|
-
})
|
|
56
|
-
.filter(Boolean) as React.ReactElement[];
|
|
57
|
-
|
|
58
|
-
if (children) {
|
|
59
|
-
return children({ messageElements, messages, isLoading });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<div className={twMerge("flex flex-col", className)} {...props}>
|
|
64
|
-
{messageElements}
|
|
65
|
-
{isLoading && renderSlot(cursor, CopilotChatMessageView.Cursor, {})}
|
|
66
|
-
</div>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
CopilotChatMessageView.Cursor = function Cursor({
|
|
71
|
-
className,
|
|
72
|
-
...props
|
|
73
|
-
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
74
|
-
return (
|
|
75
|
-
<div
|
|
76
|
-
className={twMerge(
|
|
77
|
-
"w-[11px] h-[11px] rounded-full bg-foreground animate-pulse-cursor ml-1",
|
|
78
|
-
className
|
|
79
|
-
)}
|
|
80
|
-
{...props}
|
|
81
|
-
/>
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
export default CopilotChatMessageView;
|