vox-ai-react 1.0.9 → 1.1.0
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/dist/index.esm.js +52 -80
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +52 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,23 +37,22 @@ async function decodeAudioData(arrayBuffer, ctx, sampleRate, numChannels) {
|
|
|
37
37
|
const audioBuffer = ctx.createBuffer(numChannels, numSamples, sampleRate);
|
|
38
38
|
const channelData = audioBuffer.getChannelData(0);
|
|
39
39
|
for (let i = 0; i < numSamples; i++) {
|
|
40
|
-
|
|
41
|
-
channelData[i] = sample / 32768;
|
|
40
|
+
channelData[i] = dataView.getInt16(i * 2, true) / 32768;
|
|
42
41
|
}
|
|
43
42
|
return audioBuffer;
|
|
44
43
|
}
|
|
45
|
-
// Icons
|
|
46
|
-
const
|
|
47
|
-
const MicIcon = ({ size = 14 }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", children: [jsxRuntime.jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
|
|
48
|
-
const MicOffIcon = ({ size = 40 }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", children: [jsxRuntime.jsx("line", { x1: "2", x2: "22", y1: "2", y2: "22" }), jsxRuntime.jsx("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }), jsxRuntime.jsx("path", { d: "M5 10v2a7 7 0 0 0 12 5" }), jsxRuntime.jsx("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }), jsxRuntime.jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }), jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
|
|
49
|
-
const XIcon = () => (jsxRuntime.
|
|
50
|
-
const PhoneOffIcon = () => (jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntime.jsx("path", { d: "M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-3.33-2.67m-2.67-3.34a19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91" }), jsxRuntime.jsx("line", { x1: "22", x2: "2", y1: "2", y2: "22" })] }));
|
|
51
|
-
const BotIcon = () => (jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntime.jsx("path", { d: "M12 8V4H8" }), jsxRuntime.jsx("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), jsxRuntime.jsx("path", { d: "M2 14h2" }), jsxRuntime.jsx("path", { d: "M20 14h2" }), jsxRuntime.jsx("path", { d: "M15 13v2" }), jsxRuntime.jsx("path", { d: "M9 13v2" })] }));
|
|
52
|
-
const UserIcon = () => (jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntime.jsx("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }), jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })] }));
|
|
53
|
-
const
|
|
54
|
-
const
|
|
44
|
+
// Icons - matching exact website icons
|
|
45
|
+
const MessageCircleIcon = () => (jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z" }) }));
|
|
46
|
+
const MicIcon = ({ size = 14 }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
|
|
47
|
+
const MicOffIcon = ({ size = 40 }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "2", x2: "22", y1: "2", y2: "22" }), jsxRuntime.jsx("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }), jsxRuntime.jsx("path", { d: "M5 10v2a7 7 0 0 0 12 5" }), jsxRuntime.jsx("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }), jsxRuntime.jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }), jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
|
|
48
|
+
const XIcon = () => (jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M18 6 6 18" }), jsxRuntime.jsx("path", { d: "m6 6 12 12" })] }));
|
|
49
|
+
const PhoneOffIcon = () => (jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-3.33-2.67m-2.67-3.34a19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91" }), jsxRuntime.jsx("line", { x1: "22", x2: "2", y1: "2", y2: "22" })] }));
|
|
50
|
+
const BotIcon = () => (jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M12 8V4H8" }), jsxRuntime.jsx("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), jsxRuntime.jsx("path", { d: "M2 14h2" }), jsxRuntime.jsx("path", { d: "M20 14h2" }), jsxRuntime.jsx("path", { d: "M15 13v2" }), jsxRuntime.jsx("path", { d: "M9 13v2" })] }));
|
|
51
|
+
const UserIcon = () => (jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }), jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })] }));
|
|
52
|
+
const Loader2Icon = () => (jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: 'vox-spin 1s linear infinite' }, children: jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
|
|
53
|
+
const AlertCircleIcon = () => (jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }), jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), jsxRuntime.jsx("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })] }));
|
|
55
54
|
// Visualizer Component
|
|
56
|
-
const Visualizer = ({ analyser, isActive
|
|
55
|
+
const Visualizer = ({ analyser, isActive }) => {
|
|
57
56
|
const canvasRef = react.useRef(null);
|
|
58
57
|
const animationRef = react.useRef();
|
|
59
58
|
react.useEffect(() => {
|
|
@@ -68,7 +67,7 @@ const Visualizer = ({ analyser, isActive, theme }) => {
|
|
|
68
67
|
const height = canvas.height;
|
|
69
68
|
ctx.clearRect(0, 0, width, height);
|
|
70
69
|
if (!analyser || !isActive) {
|
|
71
|
-
ctx.strokeStyle =
|
|
70
|
+
ctx.strokeStyle = '#3f3f46';
|
|
72
71
|
ctx.lineWidth = 1;
|
|
73
72
|
ctx.beginPath();
|
|
74
73
|
ctx.moveTo(0, height / 2);
|
|
@@ -80,7 +79,7 @@ const Visualizer = ({ analyser, isActive, theme }) => {
|
|
|
80
79
|
const bufferLength = analyser.frequencyBinCount;
|
|
81
80
|
const dataArray = new Uint8Array(bufferLength);
|
|
82
81
|
analyser.getByteTimeDomainData(dataArray);
|
|
83
|
-
ctx.strokeStyle =
|
|
82
|
+
ctx.strokeStyle = '#ffffff';
|
|
84
83
|
ctx.lineWidth = 1;
|
|
85
84
|
ctx.beginPath();
|
|
86
85
|
const sliceWidth = width / bufferLength;
|
|
@@ -99,15 +98,13 @@ const Visualizer = ({ analyser, isActive, theme }) => {
|
|
|
99
98
|
animationRef.current = requestAnimationFrame(draw);
|
|
100
99
|
};
|
|
101
100
|
draw();
|
|
102
|
-
return () => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}, [analyser, isActive, theme]);
|
|
107
|
-
return jsxRuntime.jsx("canvas", { ref: canvasRef, width: 300, height: 50, className: "vox-visualizer" });
|
|
101
|
+
return () => { if (animationRef.current)
|
|
102
|
+
cancelAnimationFrame(animationRef.current); };
|
|
103
|
+
}, [analyser, isActive]);
|
|
104
|
+
return jsxRuntime.jsx("canvas", { ref: canvasRef, width: 300, height: 50, style: { width: '100%', height: '50px', background: 'transparent' } });
|
|
108
105
|
};
|
|
109
106
|
// Text Interface Component
|
|
110
|
-
const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction
|
|
107
|
+
const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction }) => {
|
|
111
108
|
const [messages, setMessages] = react.useState([
|
|
112
109
|
{ id: 'welcome', role: 'assistant', text: `VOX INITIALIZED. GREETINGS. I AM ${agentName}. HOW MAY I ASSIST YOUR INQUIRY?` }
|
|
113
110
|
]);
|
|
@@ -123,16 +120,13 @@ const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction, theme })
|
|
|
123
120
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
|
124
121
|
if (!input.trim() || isLoading)
|
|
125
122
|
return;
|
|
126
|
-
const messageText = input;
|
|
123
|
+
const messageText = input;
|
|
127
124
|
const userMsg = { id: Date.now().toString(), role: 'user', text: messageText };
|
|
128
125
|
setMessages(prev => [...prev, userMsg]);
|
|
129
126
|
setInput('');
|
|
130
127
|
setIsLoading(true);
|
|
131
128
|
try {
|
|
132
|
-
|
|
133
|
-
const history = messages
|
|
134
|
-
.filter(m => m.id !== 'welcome')
|
|
135
|
-
.map(m => ({ role: m.role === 'assistant' ? 'model' : 'user', text: m.text }));
|
|
129
|
+
const history = messages.filter(m => m.id !== 'welcome').map(m => ({ role: m.role === 'assistant' ? 'model' : 'user', text: m.text }));
|
|
136
130
|
const response = await fetch(`${apiUrl}/api/v1/chat`, {
|
|
137
131
|
method: 'POST',
|
|
138
132
|
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
|
|
@@ -155,29 +149,26 @@ const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction, theme })
|
|
|
155
149
|
setIsLoading(false);
|
|
156
150
|
}
|
|
157
151
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
164
|
-
border: `1px solid ${msg.role === 'user' ? textColor : borderColor}`,
|
|
165
|
-
background: msg.role === 'user' ? textColor : 'transparent', color: msg.role === 'user' ? bgColor : textColor
|
|
152
|
+
return (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', background: '#000000' }, children: [jsxRuntime.jsxs("div", { ref: scrollRef, style: { flex: 1, overflowY: 'auto', padding: '24px', display: 'flex', flexDirection: 'column', gap: '24px' }, children: [messages.map((msg) => (jsxRuntime.jsx("div", { style: { display: 'flex', justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start' }, children: jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: msg.role === 'user' ? 'row-reverse' : 'row', alignItems: 'flex-start', gap: '12px', maxWidth: '85%' }, children: [jsxRuntime.jsx("div", { style: {
|
|
153
|
+
flexShrink: 0, width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
154
|
+
border: msg.role === 'user' ? '1px solid #ffffff' : '1px solid #52525b',
|
|
155
|
+
background: msg.role === 'user' ? '#ffffff' : 'transparent',
|
|
156
|
+
color: msg.role === 'user' ? '#000000' : '#ffffff'
|
|
166
157
|
}, children: msg.role === 'user' ? jsxRuntime.jsx(UserIcon, {}) : jsxRuntime.jsx(BotIcon, {}) }), jsxRuntime.jsx("div", { style: {
|
|
167
158
|
padding: '12px 16px', fontSize: '11px', fontWeight: 700, letterSpacing: '0.05em', lineHeight: 1.6,
|
|
168
|
-
border:
|
|
169
|
-
color: msg.role === 'user' ?
|
|
170
|
-
}, children: msg.text })] }) }, msg.id))), isLoading && (jsxRuntime.jsx("div", { style: { display: 'flex', justifyContent: 'flex-start' }, children: jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: '
|
|
171
|
-
flex: 1, padding: '16px 24px', border:
|
|
159
|
+
border: '1px solid #3f3f46', background: msg.role === 'user' ? '#27272a' : '#000000',
|
|
160
|
+
color: msg.role === 'user' ? '#ffffff' : '#d4d4d8'
|
|
161
|
+
}, children: msg.text })] }) }, msg.id))), isLoading && (jsxRuntime.jsx("div", { style: { display: 'flex', justifyContent: 'flex-start' }, children: jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', gap: '12px' }, children: [jsxRuntime.jsx("div", { style: { flexShrink: 0, width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid #3f3f46', color: '#ffffff', opacity: 0.2 }, children: jsxRuntime.jsx(BotIcon, {}) }), jsxRuntime.jsx("div", { style: { padding: '16px 24px', border: '1px solid #3f3f46', background: '#000000', color: '#71717a' }, children: jsxRuntime.jsx(Loader2Icon, {}) })] }) }))] }), jsxRuntime.jsx("div", { style: { padding: '24px' }, children: jsxRuntime.jsxs("form", { onSubmit: handleSend, style: { display: 'flex', gap: '16px' }, children: [jsxRuntime.jsx("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: "INPUT COMMAND...", disabled: isLoading, style: {
|
|
162
|
+
flex: 1, padding: '16px 24px', border: '1px solid #52525b', background: '#000000', color: '#ffffff',
|
|
172
163
|
outline: 'none', fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.1em'
|
|
173
164
|
} }), jsxRuntime.jsx("button", { type: "submit", disabled: !input.trim() || isLoading, style: {
|
|
174
|
-
padding: '16px 24px', background:
|
|
165
|
+
padding: '16px 24px', background: '#ffffff', color: '#000000', border: 'none', cursor: 'pointer',
|
|
175
166
|
fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.1em',
|
|
176
167
|
opacity: !input.trim() || isLoading ? 0.2 : 1
|
|
177
168
|
}, children: "EXECUTE" })] }) })] }));
|
|
178
169
|
};
|
|
179
170
|
// Voice Interface Component
|
|
180
|
-
const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction,
|
|
171
|
+
const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall }) => {
|
|
181
172
|
const [connectionState, setConnectionState] = react.useState(ConnectionState.DISCONNECTED);
|
|
182
173
|
const [errorMsg, setErrorMsg] = react.useState('');
|
|
183
174
|
const [isMuted, setIsMuted] = react.useState(false);
|
|
@@ -299,14 +290,12 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, theme, onE
|
|
|
299
290
|
monitorSpeaking();
|
|
300
291
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
301
292
|
streamRef.current = stream;
|
|
302
|
-
// Connect to Gemini Live API via WebSocket
|
|
303
293
|
const wsUrl = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key=${geminiApiKey}`;
|
|
304
294
|
console.log('[VOX] Connecting to Gemini Live API...');
|
|
305
295
|
const ws = new WebSocket(wsUrl);
|
|
306
296
|
sessionRef.current = ws;
|
|
307
297
|
ws.onopen = () => {
|
|
308
298
|
console.log('[VOX] WebSocket connected, sending setup...');
|
|
309
|
-
// Send setup message
|
|
310
299
|
const setupMsg = {
|
|
311
300
|
setup: {
|
|
312
301
|
model: 'models/gemini-2.5-flash-native-audio-preview-09-2025',
|
|
@@ -319,14 +308,12 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, theme, onE
|
|
|
319
308
|
ws.onmessage = async (event) => {
|
|
320
309
|
var _a, _b, _c, _d, _e, _f;
|
|
321
310
|
let data;
|
|
322
|
-
// Handle both text and blob responses
|
|
323
311
|
if (event.data instanceof Blob) {
|
|
324
312
|
const text = await event.data.text();
|
|
325
313
|
try {
|
|
326
314
|
data = JSON.parse(text);
|
|
327
315
|
}
|
|
328
316
|
catch (e) {
|
|
329
|
-
console.log('[VOX] Non-JSON blob received');
|
|
330
317
|
return;
|
|
331
318
|
}
|
|
332
319
|
}
|
|
@@ -335,7 +322,6 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, theme, onE
|
|
|
335
322
|
data = JSON.parse(event.data);
|
|
336
323
|
}
|
|
337
324
|
catch (e) {
|
|
338
|
-
console.log('[VOX] Non-JSON message received');
|
|
339
325
|
return;
|
|
340
326
|
}
|
|
341
327
|
}
|
|
@@ -343,7 +329,6 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, theme, onE
|
|
|
343
329
|
if (data.setupComplete) {
|
|
344
330
|
console.log('[VOX] Setup complete, voice ready!');
|
|
345
331
|
setConnectionState(ConnectionState.CONNECTED);
|
|
346
|
-
// Setup audio input pipeline
|
|
347
332
|
const source = inputCtx.createMediaStreamSource(stream);
|
|
348
333
|
sourceNodeRef.current = source;
|
|
349
334
|
const processor = inputCtx.createScriptProcessor(2048, 1, 1);
|
|
@@ -359,7 +344,6 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, theme, onE
|
|
|
359
344
|
source.connect(processor);
|
|
360
345
|
processor.connect(inputCtx.destination);
|
|
361
346
|
}
|
|
362
|
-
// Handle audio output
|
|
363
347
|
const audioData = (_e = (_d = (_c = (_b = (_a = data.serverContent) === null || _a === void 0 ? void 0 : _a.modelTurn) === null || _b === void 0 ? void 0 : _b.parts) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.inlineData) === null || _e === void 0 ? void 0 : _e.data;
|
|
364
348
|
if (audioData) {
|
|
365
349
|
const ctx = outputAudioContextRef.current;
|
|
@@ -422,20 +406,13 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, theme, onE
|
|
|
422
406
|
connect();
|
|
423
407
|
return () => disconnect();
|
|
424
408
|
}, []);
|
|
425
|
-
|
|
426
|
-
const textColor = theme === 'light' ? '#000000' : '#ffffff';
|
|
427
|
-
const borderColor = theme === 'light' ? '#a1a1aa' : '#52525b';
|
|
428
|
-
const mutedColor = '#71717a';
|
|
429
|
-
return (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '32px', height: '100%', padding: '24px' }, children: [jsxRuntime.jsxs("div", { style: { position: 'relative' }, children: [connectionState === ConnectionState.CONNECTED && isSpeaking && (jsxRuntime.jsx("div", { style: {
|
|
430
|
-
position: 'absolute', inset: 0, borderRadius: '50%', border: `1px solid ${textColor}`,
|
|
431
|
-
transform: `scale(${1 + speakingIntensity * 0.3})`, opacity: 0.2, animation: 'vox-ping 1s infinite'
|
|
432
|
-
} })), jsxRuntime.jsx("div", { style: {
|
|
409
|
+
return (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '32px', height: '100%', padding: '24px' }, children: [jsxRuntime.jsxs("div", { style: { position: 'relative' }, children: [connectionState === ConnectionState.CONNECTED && isSpeaking && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { style: { position: 'absolute', inset: 0, borderRadius: '50%', border: '1px solid rgba(255,255,255,0.2)', transform: `scale(${1 + speakingIntensity * 0.3})`, animation: 'vox-ping 1s infinite' } }), jsxRuntime.jsx("div", { style: { position: 'absolute', inset: 0, borderRadius: '50%', border: '1px solid rgba(255,255,255,0.1)', transform: `scale(${1 + speakingIntensity * 0.5})`, transition: 'transform 0.1s ease-out' } })] })), jsxRuntime.jsx("div", { style: {
|
|
433
410
|
width: '112px', height: '112px', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
434
|
-
border: `1px solid ${connectionState === ConnectionState.CONNECTED ?
|
|
435
|
-
boxShadow: connectionState === ConnectionState.CONNECTED ? `0 0 ${30 + speakingIntensity * 40}px
|
|
411
|
+
border: `1px solid ${connectionState === ConnectionState.CONNECTED ? '#ffffff' : '#52525b'}`,
|
|
412
|
+
boxShadow: connectionState === ConnectionState.CONNECTED ? `0 0 ${30 + speakingIntensity * 40}px rgba(255,255,255,${0.15 + speakingIntensity * 0.25})` : 'none',
|
|
436
413
|
transform: isSpeaking ? `scale(${1 + speakingIntensity * 0.08})` : 'scale(1)', transition: 'all 0.3s'
|
|
437
|
-
}, children: connectionState === ConnectionState.CONNECTING ? (jsxRuntime.jsx("div", { style: { width: '40px', height: '40px', border:
|
|
438
|
-
padding: '12px 24px', border:
|
|
414
|
+
}, children: connectionState === ConnectionState.CONNECTING ? (jsxRuntime.jsx("div", { style: { width: '40px', height: '40px', border: '2px solid #ffffff', borderTopColor: 'transparent', borderRadius: '50%', animation: 'vox-spin 1s linear infinite' } })) : (jsxRuntime.jsx("div", { style: { padding: '20px', color: isMuted ? '#52525b' : '#ffffff' }, children: isMuted ? jsxRuntime.jsx(MicOffIcon, {}) : jsxRuntime.jsx(MicIcon, { size: 40 }) })) }), connectionState === ConnectionState.CONNECTED && (jsxRuntime.jsxs("span", { style: { position: 'absolute', bottom: '-4px', right: '-4px', display: 'flex', width: '12px', height: '12px' }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', display: 'inline-flex', width: '100%', height: '100%', borderRadius: '50%', background: '#ffffff', opacity: isSpeaking ? 1 : 0.75, animation: isSpeaking ? 'vox-ping 1s infinite' : 'none' } }), jsxRuntime.jsx("span", { style: { position: 'relative', display: 'inline-flex', borderRadius: '50%', width: '12px', height: '12px', background: '#ffffff' } })] }))] }), jsxRuntime.jsxs("div", { style: { width: '100%', maxWidth: '280px', display: 'flex', flexDirection: 'column', gap: '16px' }, children: [jsxRuntime.jsxs("p", { style: { textAlign: 'center', fontSize: '10px', fontWeight: 900, color: '#71717a', textTransform: 'uppercase', letterSpacing: '0.2em', height: '20px' }, children: [connectionState === ConnectionState.CONNECTING && 'Initializing...', connectionState === ConnectionState.CONNECTED && (isSpeaking ? 'Agent Speaking...' : isMuted ? 'Signal Muted' : 'Listening...'), connectionState === ConnectionState.ERROR && 'Connection Error', connectionState === ConnectionState.DISCONNECTED && 'Disconnected'] }), connectionState === ConnectionState.CONNECTED && callDuration > 0 && (jsxRuntime.jsxs("p", { style: { textAlign: 'center', fontSize: '11px', fontFamily: 'monospace', color: '#ffffff' }, children: [Math.floor(callDuration / 60).toString().padStart(2, '0'), ":", (callDuration % 60).toString().padStart(2, '0')] })), jsxRuntime.jsx(Visualizer, { analyser: inputAnalyserRef.current, isActive: connectionState === ConnectionState.CONNECTED && !isMuted })] }), connectionState === ConnectionState.ERROR && (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.1em', padding: '12px 16px', maxWidth: '280px', textAlign: 'center', color: '#71717a', background: '#18181b', border: '1px solid #3f3f46' }, children: [jsxRuntime.jsx(AlertCircleIcon, {}), jsxRuntime.jsx("span", { style: { marginLeft: '12px' }, children: errorMsg })] })), connectionState === ConnectionState.CONNECTED && (jsxRuntime.jsxs("button", { onClick: () => { disconnect(); onEndCall === null || onEndCall === void 0 ? void 0 : onEndCall(); }, style: {
|
|
415
|
+
padding: '12px 24px', border: '1px solid #3f3f46', background: '#000000', color: '#71717a', cursor: 'pointer',
|
|
439
416
|
fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.1em', display: 'flex', alignItems: 'center', gap: '8px'
|
|
440
417
|
}, children: [jsxRuntime.jsx(PhoneOffIcon, {}), "End Call"] }))] }));
|
|
441
418
|
};
|
|
@@ -444,14 +421,8 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
|
|
|
444
421
|
const [isOpen, setIsOpen] = react.useState(false);
|
|
445
422
|
const [activeTab, setActiveTab] = react.useState('text');
|
|
446
423
|
const [isVoiceActive, setIsVoiceActive] = react.useState(false);
|
|
447
|
-
// Invert theme for widget (black in light mode, white in dark mode)
|
|
448
|
-
const widgetTheme = theme === 'light' ? 'dark' : 'light';
|
|
449
|
-
const bgColor = widgetTheme === 'light' ? '#ffffff' : '#000000';
|
|
450
|
-
const textColor = widgetTheme === 'light' ? '#000000' : '#ffffff';
|
|
451
|
-
const borderColor = widgetTheme === 'light' ? '#a1a1aa' : '#52525b';
|
|
452
|
-
const mutedColor = '#71717a';
|
|
453
424
|
const handleOpen = () => { setIsOpen(true); onOpen === null || onOpen === void 0 ? void 0 : onOpen(); };
|
|
454
|
-
const handleClose = () => { setIsOpen(false); setIsVoiceActive(false); onClose === null || onClose === void 0 ? void 0 : onClose(); };
|
|
425
|
+
const handleClose = () => { setIsOpen(false); setIsVoiceActive(false); setActiveTab('text'); onClose === null || onClose === void 0 ? void 0 : onClose(); };
|
|
455
426
|
const handleTabChange = (tab) => {
|
|
456
427
|
if (tab === 'voice' && activeTab !== 'voice') {
|
|
457
428
|
setActiveTab('voice');
|
|
@@ -462,9 +433,14 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
|
|
|
462
433
|
setActiveTab('text');
|
|
463
434
|
}
|
|
464
435
|
};
|
|
436
|
+
const handleEndCall = () => {
|
|
437
|
+
setIsVoiceActive(false);
|
|
438
|
+
setActiveTab('text');
|
|
439
|
+
};
|
|
465
440
|
const positionStyles = position === 'bottom-left'
|
|
466
441
|
? { bottom: '16px', left: '16px' }
|
|
467
442
|
: { bottom: '16px', right: '16px' };
|
|
443
|
+
// Button colors based on theme
|
|
468
444
|
const btnBg = buttonColor || (theme === 'light' ? '#000000' : '#ffffff');
|
|
469
445
|
const btnIcon = iconColor || (theme === 'light' ? '#ffffff' : '#000000');
|
|
470
446
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: `
|
|
@@ -472,26 +448,22 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
|
|
|
472
448
|
@keyframes vox-spin { to { transform: rotate(360deg); } }
|
|
473
449
|
@keyframes vox-ping { 0% { transform: scale(1); opacity: 0.5; } 50% { transform: scale(1.15); opacity: 0; } 100% { transform: scale(1); opacity: 0; } }
|
|
474
450
|
.vox-widget { animation: vox-fade-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
|
|
475
|
-
.vox-spin { animation: vox-spin 1s linear infinite; }
|
|
476
|
-
.vox-visualizer { width: 100%; height: 50px; background: transparent; }
|
|
477
|
-
.vox-scrollbar::-webkit-scrollbar { display: none; }
|
|
478
|
-
.vox-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
|
479
451
|
` }), jsxRuntime.jsxs("div", { style: { position: 'fixed', zIndex: 10000, display: 'flex', flexDirection: 'column', alignItems: position === 'bottom-left' ? 'flex-start' : 'flex-end', gap: '24px', ...positionStyles }, children: [isOpen && (jsxRuntime.jsxs("div", { className: "vox-widget", style: {
|
|
480
|
-
width: 'min(calc(100vw - 32px), 400px)', height: 'min(calc(100vh - 100px), 640px)',
|
|
481
|
-
background:
|
|
482
|
-
border:
|
|
483
|
-
}, children: [jsxRuntime.jsxs("div", { style: { height: '56px', background:
|
|
452
|
+
width: 'min(calc(100vw - 32px), 400px)', height: 'min(calc(100vh - 100px), 640px)', maxHeight: '640px',
|
|
453
|
+
background: '#000000', overflow: 'hidden', display: 'flex', flexDirection: 'column',
|
|
454
|
+
border: '1px solid #3f3f46', boxShadow: '0 30px 60px -15px rgba(0,0,0,0.5)'
|
|
455
|
+
}, children: [jsxRuntime.jsxs("div", { style: { height: '56px', background: '#000000', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 16px', flexShrink: 0, borderBottom: '1px solid #3f3f46' }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' }, children: [jsxRuntime.jsx("div", { style: { width: '32px', height: '32px', border: '1px solid #52525b', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#ffffff' }, children: jsxRuntime.jsx(MessageCircleIcon, {}) }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("h2", { style: { color: '#ffffff', fontWeight: 900, fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.2em', margin: 0 }, children: agentName }), jsxRuntime.jsxs("p", { style: { color: '#a1a1aa', fontSize: '9px', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', margin: 0, display: 'flex', alignItems: 'center' }, children: [jsxRuntime.jsx("span", { style: { width: '4px', height: '4px', background: '#ffffff', borderRadius: '50%', marginRight: '8px' } }), "Active"] })] })] }), jsxRuntime.jsx("button", { onClick: handleClose, style: { background: 'none', border: 'none', color: '#a1a1aa', cursor: 'pointer', padding: '4px', transition: 'color 0.2s' }, children: jsxRuntime.jsx(XIcon, {}) })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', padding: '4px', background: '#18181b', borderBottom: '1px solid #3f3f46', flexShrink: 0 }, children: [jsxRuntime.jsx("button", { onClick: () => handleTabChange('text'), style: {
|
|
484
456
|
flex: 1, padding: '12px', fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.2em',
|
|
485
|
-
background: 'none', border: 'none', cursor: 'pointer', color: activeTab === 'text' ?
|
|
486
|
-
}, children: "Text Interface" }), jsxRuntime.jsx("div", { style: { width: '1px', background:
|
|
457
|
+
background: 'none', border: 'none', cursor: 'pointer', color: activeTab === 'text' ? '#ffffff' : '#71717a', transition: 'color 0.2s'
|
|
458
|
+
}, children: "Text Interface" }), jsxRuntime.jsx("div", { style: { width: '1px', background: '#3f3f46' } }), jsxRuntime.jsx("button", { onClick: () => handleTabChange('voice'), style: {
|
|
487
459
|
flex: 1, padding: '12px', fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.2em',
|
|
488
|
-
background: 'none', border: 'none', cursor: 'pointer', color: activeTab === 'voice' ?
|
|
489
|
-
}, children: "Voice Protocol" })] }), jsxRuntime.jsx("div", { style: { flex: 1, overflow: 'hidden', position: 'relative', background:
|
|
460
|
+
background: 'none', border: 'none', cursor: 'pointer', color: activeTab === 'voice' ? '#ffffff' : '#71717a', transition: 'color 0.2s'
|
|
461
|
+
}, children: "Voice Protocol" })] }), jsxRuntime.jsx("div", { style: { flex: 1, overflow: 'hidden', position: 'relative', background: '#000000' }, children: activeTab === 'text' ? (jsxRuntime.jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction })) : (isVoiceActive ? (jsxRuntime.jsx(VoiceInterface, { geminiApiKey: geminiApiKey, voiceName: voiceName, systemInstruction: systemInstruction, onEndCall: handleEndCall })) : (jsxRuntime.jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction }))) })] })), !isOpen && (jsxRuntime.jsx("button", { onClick: handleOpen, style: {
|
|
490
462
|
width: `${buttonSize}px`, height: `${buttonSize}px`, borderRadius: `${borderRadius}%`,
|
|
491
463
|
background: btnBg, color: btnIcon, border: 'none', cursor: 'pointer',
|
|
492
464
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
493
465
|
boxShadow: '0 4px 20px rgba(0,0,0,0.3)', transition: 'transform 0.2s'
|
|
494
|
-
}, children: jsxRuntime.jsx(
|
|
466
|
+
}, children: jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: jsxRuntime.jsx("path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z" }) }) }))] })] }));
|
|
495
467
|
};
|
|
496
468
|
|
|
497
469
|
exports.VoxChat = VoxChat;
|