@copilotkitnext/react 0.0.1
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/.turbo/turbo-build$colon$css.log +9 -0
- package/.turbo/turbo-build.log +28 -0
- package/.turbo/turbo-check-types.log +0 -0
- package/.turbo/turbo-lint.log +78 -0
- package/.turbo/turbo-test.log +79 -0
- package/LICENSE +11 -0
- package/components.json +20 -0
- package/dist/index.d.mts +363 -0
- package/dist/index.d.ts +363 -0
- package/dist/index.js +2322 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2291 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +2 -0
- package/eslint.config.mjs +11 -0
- package/package.json +84 -0
- package/postcss.config.js +7 -0
- package/src/__tests__/setup.ts +2 -0
- package/src/components/chat/CopilotChat.tsx +90 -0
- package/src/components/chat/CopilotChatAssistantMessage.tsx +478 -0
- package/src/components/chat/CopilotChatAudioRecorder.tsx +157 -0
- package/src/components/chat/CopilotChatInput.tsx +596 -0
- package/src/components/chat/CopilotChatMessageView.tsx +85 -0
- package/src/components/chat/CopilotChatToolCallsView.tsx +43 -0
- package/src/components/chat/CopilotChatUserMessage.tsx +337 -0
- package/src/components/chat/CopilotChatView.tsx +385 -0
- package/src/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +684 -0
- package/src/components/chat/__tests__/CopilotChatInput.test.tsx +531 -0
- package/src/components/chat/__tests__/setup.ts +1 -0
- package/src/components/chat/index.ts +35 -0
- package/src/components/index.ts +4 -0
- package/src/components/ui/button.tsx +123 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/use-agent-context.tsx +17 -0
- package/src/hooks/use-agent.tsx +48 -0
- package/src/hooks/use-frontend-tool.tsx +46 -0
- package/src/hooks/use-human-in-the-loop.tsx +76 -0
- package/src/hooks/use-render-tool-call.tsx +81 -0
- package/src/index.ts +4 -0
- package/src/lib/__tests__/completePartialMarkdown.test.ts +495 -0
- package/src/lib/__tests__/renderSlot.test.tsx +610 -0
- package/src/lib/slots.tsx +55 -0
- package/src/lib/utils.ts +6 -0
- package/src/providers/CopilotChatConfigurationProvider.tsx +81 -0
- package/src/providers/CopilotKitProvider.tsx +269 -0
- package/src/providers/__tests__/CopilotKitProvider.test.tsx +487 -0
- package/src/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +261 -0
- package/src/providers/index.ts +14 -0
- package/src/styles/globals.css +302 -0
- package/src/types/frontend-tool.ts +8 -0
- package/src/types/human-in-the-loop.ts +33 -0
- package/src/types/index.ts +3 -0
- package/src/types/react-tool-call-render.ts +29 -0
- package/tailwind.config.js +9 -0
- package/test.css +2355 -0
- package/tsconfig.json +23 -0
- package/tsup.config.ts +19 -0
- package/vitest.config.mjs +15 -0
|
@@ -0,0 +1,157 @@
|
|
|
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";
|