@copilotkitnext/react 0.0.3 → 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 +3 -3
- package/.turbo/turbo-build$colon$css.log +0 -9
- package/.turbo/turbo-build.log +0 -30
- package/.turbo/turbo-check-types.log +0 -7
- 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,478 +0,0 @@
|
|
|
1
|
-
import { AssistantMessage, Message } from "@ag-ui/core";
|
|
2
|
-
import { MarkdownHooks } from "react-markdown";
|
|
3
|
-
import remarkGfm from "remark-gfm";
|
|
4
|
-
import remarkMath from "remark-math";
|
|
5
|
-
import rehypePrettyCode from "rehype-pretty-code";
|
|
6
|
-
import rehypeKatex from "rehype-katex";
|
|
7
|
-
import { useState } from "react";
|
|
8
|
-
import {
|
|
9
|
-
Copy,
|
|
10
|
-
Check,
|
|
11
|
-
ThumbsUp,
|
|
12
|
-
ThumbsDown,
|
|
13
|
-
Volume2,
|
|
14
|
-
RefreshCw,
|
|
15
|
-
} from "lucide-react";
|
|
16
|
-
import { cn } from "@/lib/utils";
|
|
17
|
-
import { useCopilotChatConfiguration } from "@/providers/CopilotChatConfigurationProvider";
|
|
18
|
-
import { twMerge } from "tailwind-merge";
|
|
19
|
-
import { Button } from "@/components/ui/button";
|
|
20
|
-
import {
|
|
21
|
-
Tooltip,
|
|
22
|
-
TooltipContent,
|
|
23
|
-
TooltipTrigger,
|
|
24
|
-
} from "@/components/ui/tooltip";
|
|
25
|
-
import "katex/dist/katex.min.css";
|
|
26
|
-
import { WithSlots, renderSlot } from "@/lib/slots";
|
|
27
|
-
import { completePartialMarkdown } from "@copilotkitnext/core";
|
|
28
|
-
import CopilotChatToolCallsView from "./CopilotChatToolCallsView";
|
|
29
|
-
|
|
30
|
-
export type CopilotChatAssistantMessageProps = WithSlots<
|
|
31
|
-
{
|
|
32
|
-
markdownRenderer: typeof CopilotChatAssistantMessage.MarkdownRenderer;
|
|
33
|
-
toolbar: typeof CopilotChatAssistantMessage.Toolbar;
|
|
34
|
-
copyButton: typeof CopilotChatAssistantMessage.CopyButton;
|
|
35
|
-
thumbsUpButton: typeof CopilotChatAssistantMessage.ThumbsUpButton;
|
|
36
|
-
thumbsDownButton: typeof CopilotChatAssistantMessage.ThumbsDownButton;
|
|
37
|
-
readAloudButton: typeof CopilotChatAssistantMessage.ReadAloudButton;
|
|
38
|
-
regenerateButton: typeof CopilotChatAssistantMessage.RegenerateButton;
|
|
39
|
-
toolCallsView: typeof CopilotChatToolCallsView;
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
onThumbsUp?: (message: AssistantMessage) => void;
|
|
43
|
-
onThumbsDown?: (message: AssistantMessage) => void;
|
|
44
|
-
onReadAloud?: (message: AssistantMessage) => void;
|
|
45
|
-
onRegenerate?: (message: AssistantMessage) => void;
|
|
46
|
-
message: AssistantMessage;
|
|
47
|
-
messages?: Message[];
|
|
48
|
-
isLoading?: boolean;
|
|
49
|
-
additionalToolbarItems?: React.ReactNode;
|
|
50
|
-
toolbarVisible?: boolean;
|
|
51
|
-
} & React.HTMLAttributes<HTMLDivElement>
|
|
52
|
-
>;
|
|
53
|
-
|
|
54
|
-
export function CopilotChatAssistantMessage({
|
|
55
|
-
message,
|
|
56
|
-
messages,
|
|
57
|
-
isLoading,
|
|
58
|
-
onThumbsUp,
|
|
59
|
-
onThumbsDown,
|
|
60
|
-
onReadAloud,
|
|
61
|
-
onRegenerate,
|
|
62
|
-
additionalToolbarItems,
|
|
63
|
-
toolbarVisible = true,
|
|
64
|
-
markdownRenderer,
|
|
65
|
-
toolbar,
|
|
66
|
-
copyButton,
|
|
67
|
-
thumbsUpButton,
|
|
68
|
-
thumbsDownButton,
|
|
69
|
-
readAloudButton,
|
|
70
|
-
regenerateButton,
|
|
71
|
-
toolCallsView,
|
|
72
|
-
children,
|
|
73
|
-
className,
|
|
74
|
-
...props
|
|
75
|
-
}: CopilotChatAssistantMessageProps) {
|
|
76
|
-
const boundMarkdownRenderer = renderSlot(
|
|
77
|
-
markdownRenderer,
|
|
78
|
-
CopilotChatAssistantMessage.MarkdownRenderer,
|
|
79
|
-
{
|
|
80
|
-
content: message.content || "",
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const boundCopyButton = renderSlot(
|
|
85
|
-
copyButton,
|
|
86
|
-
CopilotChatAssistantMessage.CopyButton,
|
|
87
|
-
{
|
|
88
|
-
onClick: async () => {
|
|
89
|
-
if (message.content) {
|
|
90
|
-
try {
|
|
91
|
-
await navigator.clipboard.writeText(message.content);
|
|
92
|
-
} catch (err) {
|
|
93
|
-
console.error("Failed to copy message:", err);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const boundThumbsUpButton = renderSlot(
|
|
101
|
-
thumbsUpButton,
|
|
102
|
-
CopilotChatAssistantMessage.ThumbsUpButton,
|
|
103
|
-
{
|
|
104
|
-
onClick: onThumbsUp,
|
|
105
|
-
}
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const boundThumbsDownButton = renderSlot(
|
|
109
|
-
thumbsDownButton,
|
|
110
|
-
CopilotChatAssistantMessage.ThumbsDownButton,
|
|
111
|
-
{
|
|
112
|
-
onClick: onThumbsDown,
|
|
113
|
-
}
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const boundReadAloudButton = renderSlot(
|
|
117
|
-
readAloudButton,
|
|
118
|
-
CopilotChatAssistantMessage.ReadAloudButton,
|
|
119
|
-
{
|
|
120
|
-
onClick: onReadAloud,
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const boundRegenerateButton = renderSlot(
|
|
125
|
-
regenerateButton,
|
|
126
|
-
CopilotChatAssistantMessage.RegenerateButton,
|
|
127
|
-
{
|
|
128
|
-
onClick: onRegenerate,
|
|
129
|
-
}
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const boundToolbar = renderSlot(
|
|
133
|
-
toolbar,
|
|
134
|
-
CopilotChatAssistantMessage.Toolbar,
|
|
135
|
-
{
|
|
136
|
-
children: (
|
|
137
|
-
<div className="flex items-center gap-1">
|
|
138
|
-
{boundCopyButton}
|
|
139
|
-
{(onThumbsUp || thumbsUpButton) && boundThumbsUpButton}
|
|
140
|
-
{(onThumbsDown || thumbsDownButton) && boundThumbsDownButton}
|
|
141
|
-
{(onReadAloud || readAloudButton) && boundReadAloudButton}
|
|
142
|
-
{(onRegenerate || regenerateButton) && boundRegenerateButton}
|
|
143
|
-
{additionalToolbarItems}
|
|
144
|
-
</div>
|
|
145
|
-
),
|
|
146
|
-
}
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const boundToolCallsView = renderSlot(
|
|
150
|
-
toolCallsView,
|
|
151
|
-
CopilotChatToolCallsView,
|
|
152
|
-
{
|
|
153
|
-
message,
|
|
154
|
-
messages,
|
|
155
|
-
isLoading,
|
|
156
|
-
}
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
if (children) {
|
|
160
|
-
return (
|
|
161
|
-
<>
|
|
162
|
-
{children({
|
|
163
|
-
markdownRenderer: boundMarkdownRenderer,
|
|
164
|
-
toolbar: boundToolbar,
|
|
165
|
-
toolCallsView: boundToolCallsView,
|
|
166
|
-
copyButton: boundCopyButton,
|
|
167
|
-
thumbsUpButton: boundThumbsUpButton,
|
|
168
|
-
thumbsDownButton: boundThumbsDownButton,
|
|
169
|
-
readAloudButton: boundReadAloudButton,
|
|
170
|
-
regenerateButton: boundRegenerateButton,
|
|
171
|
-
message,
|
|
172
|
-
messages,
|
|
173
|
-
isLoading,
|
|
174
|
-
onThumbsUp,
|
|
175
|
-
onThumbsDown,
|
|
176
|
-
onReadAloud,
|
|
177
|
-
onRegenerate,
|
|
178
|
-
additionalToolbarItems,
|
|
179
|
-
toolbarVisible,
|
|
180
|
-
})}
|
|
181
|
-
</>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<div
|
|
187
|
-
className={twMerge(
|
|
188
|
-
"prose max-w-full break-words dark:prose-invert",
|
|
189
|
-
className
|
|
190
|
-
)}
|
|
191
|
-
{...props}
|
|
192
|
-
data-message-id={message.id}
|
|
193
|
-
>
|
|
194
|
-
{boundMarkdownRenderer}
|
|
195
|
-
{boundToolCallsView}
|
|
196
|
-
{toolbarVisible && boundToolbar}
|
|
197
|
-
</div>
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
202
|
-
export namespace CopilotChatAssistantMessage {
|
|
203
|
-
const InlineCode = ({
|
|
204
|
-
children,
|
|
205
|
-
...props
|
|
206
|
-
}: React.HTMLAttributes<HTMLElement>) => {
|
|
207
|
-
return (
|
|
208
|
-
<code
|
|
209
|
-
className="px-[4.8px] py-[2.5px] bg-[rgb(236,236,236)] dark:bg-gray-800 rounded text-sm font-mono font-medium! text-foreground!"
|
|
210
|
-
{...props}
|
|
211
|
-
>
|
|
212
|
-
{children}
|
|
213
|
-
</code>
|
|
214
|
-
);
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
218
|
-
const CodeBlock = ({ children, className, onClick, ...props }: any) => {
|
|
219
|
-
const { labels } = useCopilotChatConfiguration();
|
|
220
|
-
const [copied, setCopied] = useState(false);
|
|
221
|
-
|
|
222
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
|
-
const getCodeContent = (node: any): string => {
|
|
224
|
-
if (typeof node === "string") return node;
|
|
225
|
-
if (Array.isArray(node)) return node.map(getCodeContent).join("");
|
|
226
|
-
if (node?.props?.children) return getCodeContent(node.props.children);
|
|
227
|
-
return "";
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const codeContent = getCodeContent(children);
|
|
231
|
-
const language = props["data-language"] as string | undefined;
|
|
232
|
-
|
|
233
|
-
const copyToClipboard = async () => {
|
|
234
|
-
if (!codeContent.trim()) return;
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
setCopied(true);
|
|
238
|
-
setTimeout(() => setCopied(false), 2000);
|
|
239
|
-
if (onClick) {
|
|
240
|
-
onClick();
|
|
241
|
-
}
|
|
242
|
-
} catch (err) {
|
|
243
|
-
console.error("Failed to copy code:", err);
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
return (
|
|
248
|
-
<div className="relative">
|
|
249
|
-
<div className="flex items-center justify-between px-4 pr-3 py-3 text-xs">
|
|
250
|
-
{language && (
|
|
251
|
-
<span className="font-regular text-muted-foreground dark:text-white">
|
|
252
|
-
{language}
|
|
253
|
-
</span>
|
|
254
|
-
)}
|
|
255
|
-
|
|
256
|
-
<button
|
|
257
|
-
className={cn(
|
|
258
|
-
"px-2 gap-0.5 text-xs flex items-center cursor-pointer text-muted-foreground dark:text-white"
|
|
259
|
-
)}
|
|
260
|
-
onClick={copyToClipboard}
|
|
261
|
-
title={
|
|
262
|
-
copied
|
|
263
|
-
? labels.assistantMessageToolbarCopyCodeCopiedLabel
|
|
264
|
-
: `${labels.assistantMessageToolbarCopyCodeLabel} code`
|
|
265
|
-
}
|
|
266
|
-
>
|
|
267
|
-
{copied ? (
|
|
268
|
-
<Check className="h-[10px]! w-[10px]!" />
|
|
269
|
-
) : (
|
|
270
|
-
<Copy className="h-[10px]! w-[10px]!" />
|
|
271
|
-
)}
|
|
272
|
-
<span className="text-[11px]">
|
|
273
|
-
{copied
|
|
274
|
-
? labels.assistantMessageToolbarCopyCodeCopiedLabel
|
|
275
|
-
: labels.assistantMessageToolbarCopyCodeLabel}
|
|
276
|
-
</span>
|
|
277
|
-
</button>
|
|
278
|
-
</div>
|
|
279
|
-
|
|
280
|
-
<pre
|
|
281
|
-
className={cn(
|
|
282
|
-
className,
|
|
283
|
-
"rounded-2xl bg-transparent border-t-0 my-1!"
|
|
284
|
-
)}
|
|
285
|
-
{...props}
|
|
286
|
-
>
|
|
287
|
-
{children}
|
|
288
|
-
</pre>
|
|
289
|
-
</div>
|
|
290
|
-
);
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
export const MarkdownRenderer: React.FC<
|
|
294
|
-
React.HTMLAttributes<HTMLDivElement> & { content: string }
|
|
295
|
-
> = ({ content, className }) => (
|
|
296
|
-
<div className={className}>
|
|
297
|
-
<MarkdownHooks
|
|
298
|
-
/* async plugins are now fine ✨ */
|
|
299
|
-
remarkPlugins={[remarkGfm, remarkMath]}
|
|
300
|
-
rehypePlugins={[
|
|
301
|
-
[
|
|
302
|
-
rehypePrettyCode,
|
|
303
|
-
{
|
|
304
|
-
keepBackground: false,
|
|
305
|
-
theme: {
|
|
306
|
-
dark: "one-dark-pro",
|
|
307
|
-
light: "one-light",
|
|
308
|
-
},
|
|
309
|
-
bypassInlineCode: true,
|
|
310
|
-
},
|
|
311
|
-
],
|
|
312
|
-
rehypeKatex,
|
|
313
|
-
]}
|
|
314
|
-
components={{
|
|
315
|
-
pre: CodeBlock,
|
|
316
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
317
|
-
code: ({ className, children, ...props }: any) => {
|
|
318
|
-
// For inline code, use custom styling
|
|
319
|
-
if (typeof children === "string") {
|
|
320
|
-
return <InlineCode {...props}>{children}</InlineCode>;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// For code blocks, just return the code element as-is
|
|
324
|
-
return (
|
|
325
|
-
<code className={className} {...props}>
|
|
326
|
-
{children}
|
|
327
|
-
</code>
|
|
328
|
-
);
|
|
329
|
-
},
|
|
330
|
-
}}
|
|
331
|
-
>
|
|
332
|
-
{completePartialMarkdown(content || "")}
|
|
333
|
-
</MarkdownHooks>
|
|
334
|
-
</div>
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
export const Toolbar: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
|
|
338
|
-
className,
|
|
339
|
-
...props
|
|
340
|
-
}) => (
|
|
341
|
-
<div
|
|
342
|
-
className={twMerge(
|
|
343
|
-
"w-full bg-transparent flex items-center -ml-[5px] -mt-[0px]",
|
|
344
|
-
className
|
|
345
|
-
)}
|
|
346
|
-
{...props}
|
|
347
|
-
/>
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
export const ToolbarButton: React.FC<
|
|
351
|
-
React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
352
|
-
title: string;
|
|
353
|
-
children: React.ReactNode;
|
|
354
|
-
}
|
|
355
|
-
> = ({ title, children, ...props }) => {
|
|
356
|
-
return (
|
|
357
|
-
<Tooltip>
|
|
358
|
-
<TooltipTrigger asChild>
|
|
359
|
-
<Button
|
|
360
|
-
type="button"
|
|
361
|
-
variant="assistantMessageToolbarButton"
|
|
362
|
-
aria-label={title}
|
|
363
|
-
{...props}
|
|
364
|
-
>
|
|
365
|
-
{children}
|
|
366
|
-
</Button>
|
|
367
|
-
</TooltipTrigger>
|
|
368
|
-
<TooltipContent side="bottom">
|
|
369
|
-
<p>{title}</p>
|
|
370
|
-
</TooltipContent>
|
|
371
|
-
</Tooltip>
|
|
372
|
-
);
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
export const CopyButton: React.FC<
|
|
376
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
377
|
-
> = ({ className, title, onClick, ...props }) => {
|
|
378
|
-
const { labels } = useCopilotChatConfiguration();
|
|
379
|
-
const [copied, setCopied] = useState(false);
|
|
380
|
-
|
|
381
|
-
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
382
|
-
setCopied(true);
|
|
383
|
-
setTimeout(() => setCopied(false), 2000);
|
|
384
|
-
|
|
385
|
-
if (onClick) {
|
|
386
|
-
onClick(event);
|
|
387
|
-
}
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
return (
|
|
391
|
-
<ToolbarButton
|
|
392
|
-
title={title || labels.assistantMessageToolbarCopyMessageLabel}
|
|
393
|
-
onClick={handleClick}
|
|
394
|
-
className={className}
|
|
395
|
-
{...props}
|
|
396
|
-
>
|
|
397
|
-
{copied ? (
|
|
398
|
-
<Check className="size-[18px]" />
|
|
399
|
-
) : (
|
|
400
|
-
<Copy className="size-[18px]" />
|
|
401
|
-
)}
|
|
402
|
-
</ToolbarButton>
|
|
403
|
-
);
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
export const ThumbsUpButton: React.FC<
|
|
407
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
408
|
-
> = ({ title, ...props }) => {
|
|
409
|
-
const { labels } = useCopilotChatConfiguration();
|
|
410
|
-
return (
|
|
411
|
-
<ToolbarButton
|
|
412
|
-
title={title || labels.assistantMessageToolbarThumbsUpLabel}
|
|
413
|
-
{...props}
|
|
414
|
-
>
|
|
415
|
-
<ThumbsUp className="size-[18px]" />
|
|
416
|
-
</ToolbarButton>
|
|
417
|
-
);
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
export const ThumbsDownButton: React.FC<
|
|
421
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
422
|
-
> = ({ title, ...props }) => {
|
|
423
|
-
const { labels } = useCopilotChatConfiguration();
|
|
424
|
-
return (
|
|
425
|
-
<ToolbarButton
|
|
426
|
-
title={title || labels.assistantMessageToolbarThumbsDownLabel}
|
|
427
|
-
{...props}
|
|
428
|
-
>
|
|
429
|
-
<ThumbsDown className="size-[18px]" />
|
|
430
|
-
</ToolbarButton>
|
|
431
|
-
);
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
export const ReadAloudButton: React.FC<
|
|
435
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
436
|
-
> = ({ title, ...props }) => {
|
|
437
|
-
const { labels } = useCopilotChatConfiguration();
|
|
438
|
-
return (
|
|
439
|
-
<ToolbarButton
|
|
440
|
-
title={title || labels.assistantMessageToolbarReadAloudLabel}
|
|
441
|
-
{...props}
|
|
442
|
-
>
|
|
443
|
-
<Volume2 className="size-[20px]" />
|
|
444
|
-
</ToolbarButton>
|
|
445
|
-
);
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
export const RegenerateButton: React.FC<
|
|
449
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
450
|
-
> = ({ title, ...props }) => {
|
|
451
|
-
const { labels } = useCopilotChatConfiguration();
|
|
452
|
-
return (
|
|
453
|
-
<ToolbarButton
|
|
454
|
-
title={title || labels.assistantMessageToolbarRegenerateLabel}
|
|
455
|
-
{...props}
|
|
456
|
-
>
|
|
457
|
-
<RefreshCw className="size-[18px]" />
|
|
458
|
-
</ToolbarButton>
|
|
459
|
-
);
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
CopilotChatAssistantMessage.MarkdownRenderer.displayName =
|
|
464
|
-
"CopilotChatAssistantMessage.MarkdownRenderer";
|
|
465
|
-
CopilotChatAssistantMessage.Toolbar.displayName =
|
|
466
|
-
"CopilotChatAssistantMessage.Toolbar";
|
|
467
|
-
CopilotChatAssistantMessage.CopyButton.displayName =
|
|
468
|
-
"CopilotChatAssistantMessage.CopyButton";
|
|
469
|
-
CopilotChatAssistantMessage.ThumbsUpButton.displayName =
|
|
470
|
-
"CopilotChatAssistantMessage.ThumbsUpButton";
|
|
471
|
-
CopilotChatAssistantMessage.ThumbsDownButton.displayName =
|
|
472
|
-
"CopilotChatAssistantMessage.ThumbsDownButton";
|
|
473
|
-
CopilotChatAssistantMessage.ReadAloudButton.displayName =
|
|
474
|
-
"CopilotChatAssistantMessage.ReadAloudButton";
|
|
475
|
-
CopilotChatAssistantMessage.RegenerateButton.displayName =
|
|
476
|
-
"CopilotChatAssistantMessage.RegenerateButton";
|
|
477
|
-
|
|
478
|
-
export default CopilotChatAssistantMessage;
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { useRef, useEffect, useImperativeHandle, forwardRef } from "react";
|
|
2
|
-
import { twMerge } from "tailwind-merge";
|
|
3
|
-
|
|
4
|
-
/** Finite-state machine for every recorder implementation */
|
|
5
|
-
export type AudioRecorderState = "idle" | "recording" | "processing";
|
|
6
|
-
|
|
7
|
-
/** Error subclass so callers can `instanceof`-guard recorder failures */
|
|
8
|
-
export class AudioRecorderError extends Error {
|
|
9
|
-
constructor(message: string) {
|
|
10
|
-
super(message);
|
|
11
|
-
this.name = "AudioRecorderError";
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const CopilotChatAudioRecorder = forwardRef<
|
|
16
|
-
any,
|
|
17
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
18
|
-
>((props, ref) => {
|
|
19
|
-
const { className, ...divProps } = props;
|
|
20
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
21
|
-
|
|
22
|
-
// Generate fake waveform that moves with time
|
|
23
|
-
const getLoudness = (n: number): number[] => {
|
|
24
|
-
const elapsed = Date.now() / 1000; // Use current timestamp directly
|
|
25
|
-
const samples: number[] = [];
|
|
26
|
-
|
|
27
|
-
for (let i = 0; i < n; i++) {
|
|
28
|
-
// Create a position that moves from left to right over time
|
|
29
|
-
const position = (i / n) * 10 + elapsed * 0.5; // Scroll speed (slower)
|
|
30
|
-
|
|
31
|
-
// Generate waveform using multiple sine waves for realism
|
|
32
|
-
const wave1 = Math.sin(position * 2) * 0.3;
|
|
33
|
-
const wave2 = Math.sin(position * 5 + elapsed) * 0.2;
|
|
34
|
-
const wave3 = Math.sin(position * 0.5 + elapsed * 0.3) * 0.4;
|
|
35
|
-
|
|
36
|
-
// Add some randomness for natural variation
|
|
37
|
-
const noise = (Math.random() - 0.5) * 0.1;
|
|
38
|
-
|
|
39
|
-
// Combine waves and add envelope for realistic amplitude variation
|
|
40
|
-
const envelope = Math.sin(elapsed * 0.7) * 0.5 + 0.5; // Slow amplitude modulation
|
|
41
|
-
let amplitude = (wave1 + wave2 + wave3 + noise) * envelope;
|
|
42
|
-
|
|
43
|
-
// Clamp to 0-1 range
|
|
44
|
-
amplitude = Math.max(0, Math.min(1, amplitude * 0.5 + 0.3));
|
|
45
|
-
|
|
46
|
-
samples.push(amplitude);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return samples;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// No setup needed - stub implementation
|
|
53
|
-
|
|
54
|
-
// Canvas rendering with 60fps animation
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const canvas = canvasRef.current;
|
|
57
|
-
if (!canvas) return;
|
|
58
|
-
|
|
59
|
-
const ctx = canvas.getContext("2d");
|
|
60
|
-
if (!ctx) return;
|
|
61
|
-
|
|
62
|
-
let animationId: number;
|
|
63
|
-
|
|
64
|
-
const draw = () => {
|
|
65
|
-
const rect = canvas.getBoundingClientRect();
|
|
66
|
-
const dpr = window.devicePixelRatio || 1;
|
|
67
|
-
|
|
68
|
-
// Update canvas dimensions if container resized
|
|
69
|
-
if (
|
|
70
|
-
canvas.width !== rect.width * dpr ||
|
|
71
|
-
canvas.height !== rect.height * dpr
|
|
72
|
-
) {
|
|
73
|
-
canvas.width = rect.width * dpr;
|
|
74
|
-
canvas.height = rect.height * dpr;
|
|
75
|
-
ctx.scale(dpr, dpr);
|
|
76
|
-
ctx.imageSmoothingEnabled = false;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Configuration
|
|
80
|
-
const barWidth = 2;
|
|
81
|
-
const minHeight = 2;
|
|
82
|
-
const maxHeight = 20;
|
|
83
|
-
const gap = 2;
|
|
84
|
-
const numSamples = Math.ceil(rect.width / (barWidth + gap));
|
|
85
|
-
|
|
86
|
-
// Get loudness data
|
|
87
|
-
const loudnessData = getLoudness(numSamples);
|
|
88
|
-
|
|
89
|
-
// Clear canvas
|
|
90
|
-
ctx.clearRect(0, 0, rect.width, rect.height);
|
|
91
|
-
|
|
92
|
-
// Get current foreground color
|
|
93
|
-
const computedStyle = getComputedStyle(canvas);
|
|
94
|
-
const currentForeground = computedStyle.color;
|
|
95
|
-
|
|
96
|
-
// Draw bars
|
|
97
|
-
ctx.fillStyle = currentForeground;
|
|
98
|
-
const centerY = rect.height / 2;
|
|
99
|
-
|
|
100
|
-
for (let i = 0; i < loudnessData.length; i++) {
|
|
101
|
-
const sample = loudnessData[i] ?? 0;
|
|
102
|
-
const barHeight = Math.round(
|
|
103
|
-
sample * (maxHeight - minHeight) + minHeight
|
|
104
|
-
);
|
|
105
|
-
const x = Math.round(i * (barWidth + gap));
|
|
106
|
-
const y = Math.round(centerY - barHeight / 2);
|
|
107
|
-
|
|
108
|
-
ctx.fillRect(x, y, barWidth, barHeight);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
animationId = requestAnimationFrame(draw);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
draw();
|
|
115
|
-
|
|
116
|
-
return () => {
|
|
117
|
-
if (animationId) {
|
|
118
|
-
cancelAnimationFrame(animationId);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
|
-
// Expose AudioRecorder API
|
|
124
|
-
useImperativeHandle(
|
|
125
|
-
ref,
|
|
126
|
-
() => ({
|
|
127
|
-
get state() {
|
|
128
|
-
return "idle" as AudioRecorderState;
|
|
129
|
-
},
|
|
130
|
-
start: async () => {
|
|
131
|
-
// Stub implementation - no actual recording
|
|
132
|
-
},
|
|
133
|
-
stop: () =>
|
|
134
|
-
new Promise<Blob>((resolve) => {
|
|
135
|
-
// Stub implementation - return empty blob
|
|
136
|
-
const emptyBlob = new Blob([], { type: "audio/webm" });
|
|
137
|
-
resolve(emptyBlob);
|
|
138
|
-
}),
|
|
139
|
-
dispose: () => {
|
|
140
|
-
// No cleanup needed
|
|
141
|
-
},
|
|
142
|
-
}),
|
|
143
|
-
[]
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<div className={twMerge("h-[44px] w-full px-5", className)} {...divProps}>
|
|
148
|
-
<canvas
|
|
149
|
-
ref={canvasRef}
|
|
150
|
-
className="w-full h-full"
|
|
151
|
-
style={{ imageRendering: "pixelated" }}
|
|
152
|
-
/>
|
|
153
|
-
</div>
|
|
154
|
-
);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
CopilotChatAudioRecorder.displayName = "WebAudioRecorder";
|