@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.
Files changed (48) hide show
  1. package/package.json +3 -3
  2. package/.turbo/turbo-build$colon$css.log +0 -9
  3. package/.turbo/turbo-build.log +0 -30
  4. package/.turbo/turbo-check-types.log +0 -7
  5. package/.turbo/turbo-lint.log +0 -78
  6. package/.turbo/turbo-test.log +0 -79
  7. package/postcss.config.js +0 -7
  8. package/src/__tests__/setup.ts +0 -2
  9. package/src/components/chat/CopilotChat.tsx +0 -90
  10. package/src/components/chat/CopilotChatAssistantMessage.tsx +0 -478
  11. package/src/components/chat/CopilotChatAudioRecorder.tsx +0 -157
  12. package/src/components/chat/CopilotChatInput.tsx +0 -596
  13. package/src/components/chat/CopilotChatMessageView.tsx +0 -85
  14. package/src/components/chat/CopilotChatToolCallsView.tsx +0 -43
  15. package/src/components/chat/CopilotChatUserMessage.tsx +0 -337
  16. package/src/components/chat/CopilotChatView.tsx +0 -385
  17. package/src/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +0 -684
  18. package/src/components/chat/__tests__/CopilotChatInput.test.tsx +0 -531
  19. package/src/components/chat/__tests__/setup.ts +0 -1
  20. package/src/components/chat/index.ts +0 -35
  21. package/src/components/index.ts +0 -4
  22. package/src/components/ui/button.tsx +0 -123
  23. package/src/components/ui/dropdown-menu.tsx +0 -257
  24. package/src/components/ui/tooltip.tsx +0 -59
  25. package/src/hooks/index.ts +0 -6
  26. package/src/hooks/use-agent-context.tsx +0 -17
  27. package/src/hooks/use-agent.tsx +0 -48
  28. package/src/hooks/use-frontend-tool.tsx +0 -46
  29. package/src/hooks/use-human-in-the-loop.tsx +0 -76
  30. package/src/hooks/use-render-tool-call.tsx +0 -81
  31. package/src/index.ts +0 -4
  32. package/src/lib/__tests__/completePartialMarkdown.test.ts +0 -495
  33. package/src/lib/__tests__/renderSlot.test.tsx +0 -610
  34. package/src/lib/slots.tsx +0 -55
  35. package/src/lib/utils.ts +0 -6
  36. package/src/providers/CopilotChatConfigurationProvider.tsx +0 -81
  37. package/src/providers/CopilotKitProvider.tsx +0 -269
  38. package/src/providers/__tests__/CopilotKitProvider.test.tsx +0 -487
  39. package/src/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +0 -261
  40. package/src/providers/index.ts +0 -14
  41. package/src/styles/globals.css +0 -302
  42. package/src/types/frontend-tool.ts +0 -8
  43. package/src/types/human-in-the-loop.ts +0 -33
  44. package/src/types/index.ts +0 -3
  45. package/src/types/react-tool-call-render.ts +0 -29
  46. package/tailwind.config.js +0 -9
  47. package/tsconfig.json +0 -23
  48. 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";