vox-ai-react 1.0.8 → 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.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
- const sample = dataView.getInt16(i * 2, true);
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 MessageIcon = () => (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" }) }));
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.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }));
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 LoaderIcon = () => (jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "vox-spin", children: jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
54
- const AlertIcon = () => (jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntime.jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }), jsxRuntime.jsx("path", { d: "M12 9v4" }), jsxRuntime.jsx("path", { d: "M12 17h.01" })] }));
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, theme }) => {
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 = theme === 'light' ? '#a1a1aa' : '#3f3f46';
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 = theme === 'light' ? '#000000' : '#ffffff';
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
- if (animationRef.current)
104
- cancelAnimationFrame(animationRef.current);
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, theme }) => {
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; // Capture before clearing
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
- // Filter out welcome message and ensure history starts with user
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
- const bgColor = theme === 'light' ? '#ffffff' : '#000000';
159
- const textColor = theme === 'light' ? '#000000' : '#ffffff';
160
- const borderColor = theme === 'light' ? '#a1a1aa' : '#52525b';
161
- const mutedColor = theme === 'light' ? '#71717a' : '#a1a1aa';
162
- return (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', background: bgColor }, children: [jsxRuntime.jsxs("div", { ref: scrollRef, style: { flex: 1, overflowY: 'auto', padding: '24px', display: 'flex', flexDirection: 'column', gap: '16px' }, 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: {
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: `1px solid ${borderColor}`, background: msg.role === 'user' ? (theme === 'light' ? '#f4f4f5' : '#27272a') : bgColor,
169
- color: msg.role === 'user' ? textColor : mutedColor
170
- }, children: msg.text })] }) }, msg.id))), isLoading && (jsxRuntime.jsx("div", { style: { display: 'flex', justifyContent: 'flex-start' }, children: jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' }, children: [jsxRuntime.jsx("div", { style: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: `1px solid ${borderColor}`, color: textColor, opacity: 0.2 }, children: jsxRuntime.jsx(BotIcon, {}) }), jsxRuntime.jsx("div", { style: { padding: '16px 24px', border: `1px solid ${borderColor}`, background: bgColor, color: mutedColor }, children: jsxRuntime.jsx(LoaderIcon, {}) })] }) }))] }), 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: {
171
- flex: 1, padding: '16px 24px', border: `1px solid ${borderColor}`, background: bgColor, color: textColor,
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: textColor, color: bgColor, border: 'none', cursor: 'pointer',
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, theme, onEndCall }) => {
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
- const bgColor = theme === 'light' ? '#ffffff' : '#000000';
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 ? textColor : borderColor}`,
435
- boxShadow: connectionState === ConnectionState.CONNECTED ? `0 0 ${30 + speakingIntensity * 40}px ${theme === 'light' ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.15)'}` : 'none',
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: `2px solid ${textColor}`, borderTopColor: 'transparent', borderRadius: '50%', animation: 'vox-spin 1s linear infinite' } })) : (jsxRuntime.jsx("div", { style: { padding: '20px', color: isMuted ? mutedColor : textColor }, children: isMuted ? jsxRuntime.jsx(MicOffIcon, {}) : jsxRuntime.jsx(MicIcon, { size: 40 }) })) }), connectionState === ConnectionState.CONNECTED && (jsxRuntime.jsx("span", { style: { position: 'absolute', bottom: '-4px', right: '-4px', width: '12px', height: '12px', borderRadius: '50%', background: textColor } }))] }), 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: mutedColor, 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: textColor }, 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, theme: theme })] }), 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: mutedColor, background: theme === 'light' ? '#f4f4f5' : '#18181b', border: `1px solid ${borderColor}` }, children: [jsxRuntime.jsx(AlertIcon, {}), 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: {
438
- padding: '12px 24px', border: `1px solid ${borderColor}`, background: bgColor, color: mutedColor, cursor: 'pointer',
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: bgColor, overflow: 'hidden', display: 'flex', flexDirection: 'column',
482
- border: `1px solid ${borderColor}`, boxShadow: '0 30px 60px -15px rgba(0,0,0,0.5)'
483
- }, children: [jsxRuntime.jsxs("div", { style: { height: '56px', background: bgColor, display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 24px', flexShrink: 0, borderBottom: `1px solid ${borderColor}` }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' }, children: [jsxRuntime.jsx("div", { style: { width: '32px', height: '32px', border: `1px solid ${borderColor}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: textColor }, children: jsxRuntime.jsx(MicIcon, {}) }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("h2", { style: { color: textColor, fontWeight: 900, fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.2em', margin: 0 }, children: agentName }), jsxRuntime.jsxs("p", { style: { color: mutedColor, 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: textColor, borderRadius: '50%', marginRight: '8px' } }), "Active"] })] })] }), jsxRuntime.jsx("button", { onClick: handleClose, style: { background: 'none', border: 'none', color: mutedColor, cursor: 'pointer', padding: '4px' }, children: jsxRuntime.jsx(XIcon, {}) })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', padding: '4px', background: widgetTheme === 'light' ? '#f4f4f5' : '#09090b', borderBottom: `1px solid ${borderColor}`, flexShrink: 0 }, children: [jsxRuntime.jsx("button", { onClick: () => handleTabChange('text'), style: {
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' ? textColor : mutedColor
486
- }, children: "Text Interface" }), jsxRuntime.jsx("div", { style: { width: '1px', background: borderColor } }), jsxRuntime.jsx("button", { onClick: () => handleTabChange('voice'), style: {
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' ? textColor : mutedColor
489
- }, children: "Voice Protocol" })] }), jsxRuntime.jsx("div", { style: { flex: 1, overflow: 'hidden', position: 'relative', background: bgColor }, children: activeTab === 'text' ? (jsxRuntime.jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction, theme: widgetTheme })) : (isVoiceActive && jsxRuntime.jsx(VoiceInterface, { geminiApiKey: geminiApiKey, voiceName: voiceName, systemInstruction: systemInstruction, theme: widgetTheme, onEndCall: () => setIsVoiceActive(false) })) })] })), jsxRuntime.jsx("button", { onClick: isOpen ? handleClose : handleOpen, style: {
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: isOpen ? jsxRuntime.jsx(XIcon, {}) : jsxRuntime.jsx(MessageIcon, {}) })] })] }));
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;