vox-ai-react 1.4.0 → 1.6.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/dist/index.esm.js CHANGED
@@ -37,16 +37,18 @@ async function decodeAudioData(arrayBuffer, ctx, sampleRate, numChannels) {
37
37
  }
38
38
  return audioBuffer;
39
39
  }
40
- // Icons - matching exact website icons
40
+ // Icons
41
41
  const MessageCircleIcon = () => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z" }) }));
42
- const MicIcon = ({ size = 14 }) => (jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
43
- const MicOffIcon = ({ size = 40 }) => (jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "2", x2: "22", y1: "2", y2: "22" }), jsx("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }), jsx("path", { d: "M5 10v2a7 7 0 0 0 12 5" }), jsx("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }), jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }), jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
44
- const XIcon = () => (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 6 6 18" }), jsx("path", { d: "m6 6 12 12" })] }));
45
- const PhoneOffIcon = () => (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [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" }), jsx("line", { x1: "22", x2: "2", y1: "2", y2: "22" })] }));
46
- const VoxLogoIcon = () => (jsxs("svg", { width: "18", height: "18", viewBox: "0 0 48 48", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M28.764 25.966c0-4.489-1.936-5.99-4.764-5.99s-4.764 1.501-4.764 5.99m-.55 0h.55v5.324h0h-.55a1.826 1.826 0 0 1-1.826-1.826v-1.672a1.826 1.826 0 0 1 1.826-1.826m10.628 5.324h-.55h0v-5.324h.55a1.826 1.826 0 0 1 1.826 1.826v1.672a1.826 1.826 0 0 1-1.826 1.826" }), jsx("path", { d: "M24 42.476L12.938 33.58V5.524L24 16.351" }), jsx("path", { d: "M24 42.476L9.125 35.557V7.501L24 16.351" }), jsx("path", { d: "M24 42.476L5.5 38.004V9.948L24 16.351m0 26.125l11.062-8.896V5.524L24 16.351" }), jsx("path", { d: "m24 42.476l14.875-6.919V7.501L24 16.351" }), jsx("path", { d: "m24 42.476l18.5-4.472V9.948L24 16.351" })] }));
42
+ const MicIcon = ({ size = 20 }) => (jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
43
+ const MicOffIcon = ({ size = 20 }) => (jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "2", x2: "22", y1: "2", y2: "22" }), jsx("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }), jsx("path", { d: "M5 10v2a7 7 0 0 0 12 5" }), jsx("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }), jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }), jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }));
44
+ const XIcon = () => (jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 6 6 18" }), jsx("path", { d: "m6 6 12 12" })] }));
45
+ const PhoneOffIcon = () => (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [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" }), jsx("line", { x1: "22", x2: "2", y1: "2", y2: "22" })] }));
47
46
  const UserIcon = () => (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }), jsx("circle", { cx: "12", cy: "7", r: "4" })] }));
48
- const Loader2Icon = () => (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: jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
49
- const AlertCircleIcon = () => (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("circle", { cx: "12", cy: "12", r: "10" }), jsx("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), jsx("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })] }));
47
+ const BotIcon = () => (jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M12 8V4H8" }), jsx("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), jsx("path", { d: "M2 14h2" }), jsx("path", { d: "M20 14h2" }), jsx("path", { d: "M15 13v2" }), jsx("path", { d: "M9 13v2" })] }));
48
+ const SendIcon = () => (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "m22 2-7 20-4-9-9-4Z" }), jsx("path", { d: "M22 2 11 13" })] }));
49
+ const PlusIcon = ({ size = 20 }) => (jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M5 12h14" }), jsx("path", { d: "M12 5v14" })] }));
50
+ const Loader2Icon = () => (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: 'vox-spin 1s linear infinite' }, children: jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
51
+ const AlertCircleIcon = () => (jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("circle", { cx: "12", cy: "12", r: "10" }), jsx("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), jsx("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })] }));
50
52
  // Visualizer Component
51
53
  const Visualizer = ({ analyser, isActive }) => {
52
54
  const canvasRef = useRef(null);
@@ -100,9 +102,9 @@ const Visualizer = ({ analyser, isActive }) => {
100
102
  return jsx("canvas", { ref: canvasRef, width: 300, height: 50, style: { width: '100%', height: '50px', background: 'transparent' } });
101
103
  };
102
104
  // Text Interface Component
103
- const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction }) => {
105
+ const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction, logoUrl }) => {
104
106
  const [messages, setMessages] = useState([
105
- { id: 'welcome', role: 'assistant', text: `VOX INITIALIZED. GREETINGS. I AM ${agentName}. HOW MAY I ASSIST YOUR INQUIRY?` }
107
+ { id: 'welcome', role: 'assistant', text: `Hello! I'm ${agentName}. How can I help you today?` }
106
108
  ]);
107
109
  const [input, setInput] = useState('');
108
110
  const [isLoading, setIsLoading] = useState(false);
@@ -112,7 +114,6 @@ const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction }) => {
112
114
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
113
115
  }, [messages]);
114
116
  const handleSend = async (e) => {
115
- var _a;
116
117
  e === null || e === void 0 ? void 0 : e.preventDefault();
117
118
  if (!input.trim() || isLoading)
118
119
  return;
@@ -133,36 +134,58 @@ const TextInterface = ({ apiKey, apiUrl, agentName, systemInstruction }) => {
133
134
  throw new Error(errorData.error || 'API request failed');
134
135
  }
135
136
  const data = await response.json();
136
- const botMsg = { id: (Date.now() + 1).toString(), role: 'assistant', text: ((_a = data.response) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'ERROR: NO RESPONSE' };
137
+ const botMsg = { id: (Date.now() + 1).toString(), role: 'assistant', text: data.response || 'Error: No response' };
137
138
  setMessages(prev => [...prev, botMsg]);
138
139
  }
139
140
  catch (err) {
140
141
  console.error('VOX Chat Error:', err);
141
- const errorMsg = { id: (Date.now() + 1).toString(), role: 'assistant', text: `ERROR: ${err.message || 'SIGNAL LOST. RETRY.'}` };
142
+ const errorMsg = { id: (Date.now() + 1).toString(), role: 'assistant', text: `Error: ${err.message || 'Connection lost. Please try again.'}` };
142
143
  setMessages(prev => [...prev, errorMsg]);
143
144
  }
144
145
  finally {
145
146
  setIsLoading(false);
146
147
  }
147
148
  };
148
- return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', background: '#000000' }, children: [jsxs("div", { ref: scrollRef, style: { flex: 1, overflowY: 'auto', padding: '16px', display: 'flex', flexDirection: 'column', gap: '16px' }, children: [messages.map((msg) => (jsx("div", { style: { display: 'flex', justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start' }, children: jsxs("div", { style: { display: 'flex', flexDirection: msg.role === 'user' ? 'row-reverse' : 'row', alignItems: 'flex-start', gap: '8px', maxWidth: '85%' }, children: [jsx("div", { style: {
149
+ return (jsxs("div", { style: {
150
+ display: 'flex', flexDirection: 'column', height: '100%', background: '#000000', fontFamily: '"Plus Jakarta Sans", sans-serif',
151
+ WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale'
152
+ }, children: [jsxs("div", { ref: scrollRef, style: { flex: 1, overflowY: 'auto', padding: '16px', display: 'flex', flexDirection: 'column', gap: '16px' }, children: [messages.map((msg) => (jsx("div", { style: { display: 'flex', justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start' }, children: jsxs("div", { style: { display: 'flex', flexDirection: msg.role === 'user' ? 'row-reverse' : 'row', alignItems: 'flex-start', gap: '8px', maxWidth: '85%' }, children: [jsx("div", { style: {
149
153
  flexShrink: 0, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center',
150
154
  border: msg.role === 'user' ? '1px solid #ffffff' : 'none',
151
155
  background: msg.role === 'user' ? '#ffffff' : 'transparent',
152
156
  color: msg.role === 'user' ? '#000000' : '#ffffff'
153
- }, children: msg.role === 'user' ? jsx(UserIcon, {}) : jsx(VoxLogoIcon, {}) }), jsx("div", { style: {
154
- padding: '8px 12px', fontSize: '10px', fontWeight: 700, letterSpacing: '0.05em', lineHeight: 1.6,
155
- border: '1px solid #3f3f46', background: msg.role === 'user' ? '#27272a' : '#000000',
156
- color: msg.role === 'user' ? '#ffffff' : '#d4d4d8', borderRadius: '8px'
157
- }, children: msg.text })] }) }, msg.id))), isLoading && (jsx("div", { style: { display: 'flex', justifyContent: 'flex-start' }, children: jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', gap: '8px' }, children: [jsx("div", { style: { flexShrink: 0, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#ffffff', opacity: 0.2 }, children: jsx(VoxLogoIcon, {}) }), jsx("div", { style: { padding: '12px 16px', border: '1px solid #3f3f46', background: '#000000', color: '#71717a', borderRadius: '8px' }, children: jsx(Loader2Icon, {}) })] }) }))] }), jsx("div", { style: { padding: '16px' }, children: jsxs("form", { onSubmit: handleSend, style: { display: 'flex', gap: '8px' }, children: [jsx("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: "INPUT COMMAND...", disabled: isLoading, style: {
158
- flex: 1, padding: '12px 16px', border: '1px solid #52525b', background: '#000000', color: '#ffffff',
159
- outline: 'none', fontSize: '9px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.1em',
160
- borderRadius: '8px'
161
- } }), jsx("button", { type: "submit", disabled: !input.trim() || isLoading, style: {
162
- padding: '12px 16px', background: '#ffffff', color: '#000000', border: 'none', cursor: 'pointer',
163
- fontSize: '9px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.1em',
164
- opacity: !input.trim() || isLoading ? 0.2 : 1, borderRadius: '8px'
165
- }, children: "EXECUTE" })] }) })] }));
157
+ }, children: msg.role === 'user' ? jsx(UserIcon, {}) : (logoUrl ? jsx("img", { src: logoUrl, alt: agentName, style: { width: '24px', height: '24px', objectFit: 'contain' } }) : jsx(BotIcon, {})) }), jsx("div", { style: {
158
+ padding: '8px 12px', fontSize: '12px', fontWeight: 400, lineHeight: 1.625,
159
+ border: '1px solid', borderRadius: '8px',
160
+ borderColor: msg.role === 'user' ? '#52525b' : '#3f3f46',
161
+ background: msg.role === 'user' ? '#27272a' : '#000000',
162
+ color: msg.role === 'user' ? '#ffffff' : '#d4d4d8'
163
+ }, children: msg.text })] }) }, msg.id))), isLoading && (jsx("div", { style: { display: 'flex', justifyContent: 'flex-start' }, children: jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', gap: '8px' }, children: [jsx("div", { style: { flexShrink: 0, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#ffffff', opacity: 0.2 }, children: logoUrl ? jsx("img", { src: logoUrl, alt: agentName, style: { width: '24px', height: '24px', objectFit: 'contain' } }) : jsx(BotIcon, {}) }), jsx("div", { style: { padding: '12px 16px', border: '1px solid #3f3f46', background: '#000000', borderRadius: '8px' }, children: jsx(Loader2Icon, {}) })] }) }))] }), jsx("div", { style: { padding: '16px' }, children: jsx("form", { onSubmit: handleSend, style: { width: '100%' }, children: jsxs("div", { style: {
164
+ width: '100%', cursor: 'text', overflow: 'clip', backgroundClip: 'padding-box',
165
+ padding: '6px', boxShadow: '0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)',
166
+ border: '1px solid #3f3f46', transition: 'border-radius 200ms ease-out',
167
+ borderRadius: '9999px', display: 'grid',
168
+ gridTemplateColumns: 'auto 1fr auto', gridTemplateRows: 'auto 1fr auto',
169
+ gridTemplateAreas: '"header header header" "leading primary trailing" ". footer ."',
170
+ background: 'rgba(0,0,0,0.5)', boxSizing: 'border-box'
171
+ }, children: [jsx("div", { style: { gridArea: 'primary', display: 'flex', minHeight: '32px', alignItems: 'center', overflowX: 'hidden', paddingLeft: '4px', paddingRight: '4px', marginTop: '-6px', marginBottom: '-6px' }, children: jsx("div", { style: { flex: '1 1 0%', overflow: 'auto', maxHeight: '13rem' }, children: jsx("input", { type: "text", placeholder: "Type your message...", value: input, onChange: (e) => setInput(e.target.value), onKeyDown: (e) => { if (e.key === 'Enter' && !e.shiftKey) {
172
+ e.preventDefault();
173
+ handleSend();
174
+ } }, disabled: isLoading, style: {
175
+ width: '100%', minHeight: 0, resize: 'none', border: 'none', padding: 0,
176
+ fontSize: '12px', outline: 'none', background: 'transparent',
177
+ color: '#ffffff', margin: 0, fontFamily: 'inherit', lineHeight: 'normal'
178
+ } }) }) }), jsx("div", { style: { gridArea: 'leading', display: 'flex' }, children: jsx("button", { type: "button", "aria-label": "Add attachments", style: {
179
+ display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
180
+ width: '32px', height: '32px', borderRadius: '9999px', outline: 'none',
181
+ border: 'none', background: 'transparent', cursor: 'pointer', color: '#71717a',
182
+ transition: 'all 0.15s ease'
183
+ }, children: jsx(PlusIcon, { size: 20 }) }) }), jsx("div", { style: { gridArea: 'trailing', display: 'flex', alignItems: 'center', gap: '4px' }, children: jsxs("div", { style: { marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: '4px' }, children: [jsx("button", { type: "button", "aria-label": "Record audio message", style: {
184
+ display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
185
+ width: '32px', height: '32px', borderRadius: '9999px', outline: 'none',
186
+ border: 'none', background: 'transparent', cursor: 'pointer', color: '#71717a',
187
+ transition: 'all 0.15s ease'
188
+ }, children: jsx(MicIcon, { size: 16 }) }), input.trim() && (jsx("button", { type: "submit", disabled: isLoading, style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '32px', height: '32px', borderRadius: '9999px', border: 'none', background: '#ffffff', color: '#000000', cursor: 'pointer' }, children: jsx(SendIcon, {}) }))] }) })] }) }) })] }));
166
189
  };
167
190
  // Voice Interface Component
168
191
  const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall }) => {
@@ -170,30 +193,20 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall
170
193
  const [errorMsg, setErrorMsg] = useState('');
171
194
  const [isMuted, setIsMuted] = useState(false);
172
195
  const [callDuration, setCallDuration] = useState(0);
173
- const [isSpeaking, setIsSpeaking] = useState(false);
174
- const [speakingIntensity, setSpeakingIntensity] = useState(0);
175
196
  const callStartTimeRef = useRef(null);
176
197
  const durationIntervalRef = useRef(null);
177
198
  const inputAudioContextRef = useRef(null);
178
199
  const outputAudioContextRef = useRef(null);
179
200
  const inputAnalyserRef = useRef(null);
180
- const outputAnalyserRef = useRef(null);
181
201
  const outputGainRef = useRef(null);
182
202
  const nextStartTimeRef = useRef(0);
183
203
  const sourcesRef = useRef(new Set());
184
- const speakingAnimationRef = useRef(null);
185
204
  const sessionRef = useRef(null);
186
205
  const streamRef = useRef(null);
187
206
  const scriptProcessorRef = useRef(null);
188
207
  const sourceNodeRef = useRef(null);
189
208
  const cleanupAudio = useCallback(() => {
190
209
  var _a, _b, _c, _d;
191
- if (speakingAnimationRef.current) {
192
- cancelAnimationFrame(speakingAnimationRef.current);
193
- speakingAnimationRef.current = null;
194
- }
195
- setIsSpeaking(false);
196
- setSpeakingIntensity(0);
197
210
  sourcesRef.current.forEach(source => { try {
198
211
  source.stop();
199
212
  }
@@ -224,13 +237,6 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall
224
237
  catch (e) { }
225
238
  outputGainRef.current = null;
226
239
  }
227
- if (outputAnalyserRef.current) {
228
- try {
229
- outputAnalyserRef.current.disconnect();
230
- }
231
- catch (e) { }
232
- outputAnalyserRef.current = null;
233
- }
234
240
  if (((_a = inputAudioContextRef.current) === null || _a === void 0 ? void 0 : _a.state) !== 'closed') {
235
241
  try {
236
242
  (_b = inputAudioContextRef.current) === null || _b === void 0 ? void 0 : _b.close();
@@ -265,34 +271,16 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall
265
271
  const analyser = inputCtx.createAnalyser();
266
272
  analyser.fftSize = 256;
267
273
  inputAnalyserRef.current = analyser;
268
- const outputAnalyser = outputCtx.createAnalyser();
269
- outputAnalyser.fftSize = 256;
270
- outputAnalyserRef.current = outputAnalyser;
271
274
  const outputGain = outputCtx.createGain();
272
275
  outputGain.gain.value = 1;
273
276
  outputGainRef.current = outputGain;
274
- outputGain.connect(outputAnalyser);
275
- outputAnalyser.connect(outputCtx.destination);
276
- const monitorSpeaking = () => {
277
- if (!outputAnalyserRef.current)
278
- return;
279
- const dataArray = new Uint8Array(outputAnalyserRef.current.frequencyBinCount);
280
- outputAnalyserRef.current.getByteFrequencyData(dataArray);
281
- const average = dataArray.reduce((a, b) => a + b, 0) / dataArray.length;
282
- const intensity = Math.min(average / 128, 1);
283
- setIsSpeaking(intensity > 0.05);
284
- setSpeakingIntensity(intensity);
285
- speakingAnimationRef.current = requestAnimationFrame(monitorSpeaking);
286
- };
287
- monitorSpeaking();
277
+ outputGain.connect(outputCtx.destination);
288
278
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
289
279
  streamRef.current = stream;
290
280
  const wsUrl = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key=${geminiApiKey}`;
291
- console.log('[VOX] Connecting to Gemini Live API...');
292
281
  const ws = new WebSocket(wsUrl);
293
282
  sessionRef.current = ws;
294
283
  ws.onopen = () => {
295
- console.log('[VOX] WebSocket connected, sending setup...');
296
284
  const setupMsg = {
297
285
  setup: {
298
286
  model: 'models/gemini-2.5-flash-native-audio-preview-09-2025',
@@ -322,9 +310,7 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall
322
310
  return;
323
311
  }
324
312
  }
325
- console.log('[VOX] Received:', data);
326
313
  if (data.setupComplete) {
327
- console.log('[VOX] Setup complete, voice ready!');
328
314
  setConnectionState(ConnectionState.CONNECTED);
329
315
  const source = inputCtx.createMediaStreamSource(stream);
330
316
  sourceNodeRef.current = source;
@@ -365,19 +351,14 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall
365
351
  nextStartTimeRef.current = 0;
366
352
  }
367
353
  };
368
- ws.onclose = (event) => {
369
- console.log('[VOX] WebSocket closed:', event.code, event.reason);
370
- setConnectionState(ConnectionState.DISCONNECTED);
371
- };
372
- ws.onerror = (error) => {
373
- console.error('[VOX] WebSocket error:', error);
354
+ ws.onclose = () => setConnectionState(ConnectionState.DISCONNECTED);
355
+ ws.onerror = () => {
374
356
  setConnectionState(ConnectionState.ERROR);
375
357
  setErrorMsg('Connection failed. Check your Gemini API key.');
376
358
  cleanupAudio();
377
359
  };
378
360
  }
379
361
  catch (err) {
380
- console.error('[VOX] Connection error:', err);
381
362
  setConnectionState(ConnectionState.ERROR);
382
363
  setErrorMsg(err.message || 'Failed to access microphone or connect.');
383
364
  cleanupAudio();
@@ -403,15 +384,24 @@ const VoiceInterface = ({ geminiApiKey, voiceName, systemInstruction, onEndCall
403
384
  connect();
404
385
  return () => disconnect();
405
386
  }, []);
406
- return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '32px', height: '100%', padding: '24px' }, children: [jsxs("div", { style: { position: 'relative' }, children: [connectionState === ConnectionState.CONNECTED && isSpeaking && (jsxs(Fragment, { children: [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' } }), 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' } })] })), jsx("div", { style: {
387
+ const toggleMute = () => {
388
+ setIsMuted(!isMuted);
389
+ if (streamRef.current)
390
+ streamRef.current.getAudioTracks().forEach(track => { track.enabled = isMuted; });
391
+ };
392
+ return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '32px', height: '100%', padding: '24px' }, children: [jsxs("div", { style: { position: 'relative' }, children: [jsx("div", { style: {
407
393
  width: '112px', height: '112px', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
408
394
  border: `1px solid ${connectionState === ConnectionState.CONNECTED ? '#ffffff' : '#52525b'}`,
409
- boxShadow: connectionState === ConnectionState.CONNECTED ? `0 0 ${30 + speakingIntensity * 40}px rgba(255,255,255,${0.15 + speakingIntensity * 0.25})` : 'none',
410
- transform: isSpeaking ? `scale(${1 + speakingIntensity * 0.08})` : 'scale(1)', transition: 'all 0.3s'
411
- }, children: connectionState === ConnectionState.CONNECTING ? (jsx("div", { style: { width: '40px', height: '40px', border: '2px solid #ffffff', borderTopColor: 'transparent', borderRadius: '50%', animation: 'vox-spin 1s linear infinite' } })) : (jsx("div", { style: { padding: '20px', color: isMuted ? '#52525b' : '#ffffff' }, children: isMuted ? jsx(MicOffIcon, {}) : jsx(MicIcon, { size: 40 }) })) }), connectionState === ConnectionState.CONNECTED && (jsxs("span", { style: { position: 'absolute', bottom: '-4px', right: '-4px', display: 'flex', width: '12px', height: '12px' }, children: [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' } }), jsx("span", { style: { position: 'relative', display: 'inline-flex', borderRadius: '50%', width: '12px', height: '12px', background: '#ffffff' } })] }))] }), jsxs("div", { style: { width: '100%', maxWidth: '280px', display: 'flex', flexDirection: 'column', gap: '16px' }, children: [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 && (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')] })), jsx(Visualizer, { analyser: inputAnalyserRef.current, isActive: connectionState === ConnectionState.CONNECTED && !isMuted })] }), connectionState === ConnectionState.ERROR && (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: [jsx(AlertCircleIcon, {}), jsx("span", { style: { marginLeft: '12px' }, children: errorMsg })] })), connectionState === ConnectionState.CONNECTED && (jsxs("button", { onClick: () => { disconnect(); onEndCall === null || onEndCall === void 0 ? void 0 : onEndCall(); }, style: {
412
- padding: '12px 24px', border: '1px solid #3f3f46', background: '#000000', color: '#71717a', cursor: 'pointer',
413
- fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.1em', display: 'flex', alignItems: 'center', gap: '8px'
414
- }, children: [jsx(PhoneOffIcon, {}), "End Call"] }))] }));
395
+ boxShadow: connectionState === ConnectionState.CONNECTED ? '0 0 30px rgba(255,255,255,0.15)' : 'none',
396
+ transition: 'all 0.3s'
397
+ }, children: connectionState === ConnectionState.CONNECTING ? (jsx("div", { style: { width: '40px', height: '40px', border: '2px solid #ffffff', borderTopColor: 'transparent', borderRadius: '50%', animation: 'vox-spin 1s linear infinite' } })) : (jsx("div", { style: { padding: '20px', color: isMuted ? '#52525b' : '#ffffff' }, children: isMuted ? jsx(MicOffIcon, {}) : jsx(MicIcon, {}) })) }), connectionState === ConnectionState.CONNECTED && (jsxs("span", { style: { position: 'absolute', bottom: '-4px', right: '-4px', display: 'flex', width: '12px', height: '12px' }, children: [jsx("span", { style: { position: 'absolute', display: 'inline-flex', width: '100%', height: '100%', borderRadius: '50%', background: '#ffffff', opacity: 0.75 } }), jsx("span", { style: { position: 'relative', display: 'inline-flex', borderRadius: '50%', width: '12px', height: '12px', background: '#ffffff' } })] }))] }), jsxs("div", { style: { width: '100%', maxWidth: '280px', display: 'flex', flexDirection: 'column', gap: '16px' }, children: [jsxs("p", { style: { textAlign: 'center', fontSize: '14px', fontWeight: 600, color: '#71717a', height: '20px' }, children: [connectionState === ConnectionState.CONNECTING && 'Initializing...', connectionState === ConnectionState.CONNECTED && (isMuted ? 'Muted' : 'Listening...'), connectionState === ConnectionState.ERROR && 'Connection error', connectionState === ConnectionState.DISCONNECTED && 'Disconnected'] }), connectionState === ConnectionState.CONNECTED && callDuration > 0 && (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')] })), jsx(Visualizer, { analyser: inputAnalyserRef.current, isActive: connectionState === ConnectionState.CONNECTED && !isMuted })] }), connectionState === ConnectionState.ERROR && (jsxs("div", { style: { display: 'flex', alignItems: 'center', fontSize: '14px', fontWeight: 600, padding: '12px 16px', maxWidth: '280px', textAlign: 'center', color: '#71717a', background: '#18181b', border: '1px solid #3f3f46', borderRadius: '8px', gap: '12px' }, children: [jsx(AlertCircleIcon, {}), jsx("span", { children: errorMsg })] })), connectionState === ConnectionState.CONNECTED && (jsxs("div", { style: { display: 'flex', gap: '16px' }, children: [jsx("button", { onClick: toggleMute, style: {
398
+ padding: '16px', borderRadius: '50%', border: 'none', cursor: 'pointer', transition: 'all 0.2s',
399
+ background: isMuted ? '#27272a' : '#ffffff',
400
+ color: isMuted ? '#71717a' : '#000000'
401
+ }, children: isMuted ? jsx(MicOffIcon, {}) : jsx(MicIcon, {}) }), jsx("button", { onClick: () => { disconnect(); onEndCall === null || onEndCall === void 0 ? void 0 : onEndCall(); }, style: {
402
+ padding: '16px', borderRadius: '50%', border: 'none', cursor: 'pointer', transition: 'all 0.2s',
403
+ background: '#dc2626', color: '#ffffff'
404
+ }, children: jsx(PhoneOffIcon, {}) })] }))] }));
415
405
  };
416
406
  // Main VoxChat Component
417
407
  const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', agentName: propAgentName = 'VOX-01', voiceName: propVoiceName = 'Kore', systemInstruction: propSystemInstruction = 'You are a helpful AI assistant.', position = 'bottom-right', theme = 'dark', buttonColor, buttonSize = 56, borderRadius = 0, iconColor, onOpen, onClose, useServerConfig = false, configPollInterval = 10000 }) => {
@@ -419,11 +409,10 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
419
409
  const [isOpen, setIsOpen] = useState(false);
420
410
  const [activeTab, setActiveTab] = useState('text');
421
411
  const [isVoiceActive, setIsVoiceActive] = useState(false);
422
- // Server config state
423
412
  const [serverConfig, setServerConfig] = useState(null);
413
+ const [serverGeminiApiKey, setServerGeminiApiKey] = useState();
424
414
  const [configLoaded, setConfigLoaded] = useState(!useServerConfig);
425
415
  const [isInitialLoad, setIsInitialLoad] = useState(true);
426
- // Fetch config from server
427
416
  const fetchConfig = useCallback(() => {
428
417
  if (useServerConfig && apiKey && apiUrl) {
429
418
  fetch(`${apiUrl}/api/v1/config`, {
@@ -434,6 +423,9 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
434
423
  if (data.agentConfig) {
435
424
  setServerConfig(data.agentConfig);
436
425
  }
426
+ if (data.geminiApiKey) {
427
+ setServerGeminiApiKey(data.geminiApiKey);
428
+ }
437
429
  setConfigLoaded(true);
438
430
  setIsInitialLoad(false);
439
431
  })
@@ -444,22 +436,16 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
444
436
  });
445
437
  }
446
438
  }, [useServerConfig, apiKey, apiUrl]);
447
- // Initial fetch and real-time polling (every 10 seconds)
448
439
  useEffect(() => {
449
440
  if (useServerConfig && apiKey && apiUrl) {
450
- // Fetch immediately
451
441
  fetchConfig();
452
- // Poll at specified interval for real-time updates
453
442
  const intervalId = setInterval(fetchConfig, configPollInterval);
454
- // Cleanup interval on unmount
455
443
  return () => clearInterval(intervalId);
456
444
  }
457
445
  }, [useServerConfig, apiKey, apiUrl, fetchConfig, configPollInterval]);
458
- // Use server config if available, otherwise use props
459
446
  const agentName = (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.name) || propAgentName;
460
447
  const voiceName = (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.voiceName) || propVoiceName;
461
448
  const systemInstruction = (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.systemInstruction) || propSystemInstruction;
462
- // Widget style from server or props
463
449
  const finalButtonColor = ((_a = serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.widgetStyle) === null || _a === void 0 ? void 0 : _a.buttonColor) || buttonColor || (theme === 'light' ? '#000000' : '#ffffff');
464
450
  const finalButtonSize = ((_b = serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.widgetStyle) === null || _b === void 0 ? void 0 : _b.buttonSize) || buttonSize || 56;
465
451
  const finalBorderRadius = ((_c = serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.widgetStyle) === null || _c === void 0 ? void 0 : _c.borderRadius) !== undefined ? serverConfig.widgetStyle.borderRadius : (borderRadius !== undefined ? borderRadius : 50);
@@ -485,22 +471,28 @@ const VoxChat = ({ apiKey, geminiApiKey, apiUrl = 'https://your-server.com', age
485
471
  const positionStyles = finalPosition === 'bottom-left'
486
472
  ? { bottom: '16px', left: '16px' }
487
473
  : { bottom: '16px', right: '16px' };
488
- return (jsxs(Fragment, { children: [jsx("style", { children: `
489
- @keyframes vox-fade-in { from { opacity: 0; transform: translateY(40px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } }
490
- @keyframes vox-spin { to { transform: rotate(360deg); } }
491
- @keyframes vox-ping { 0% { transform: scale(1); opacity: 0.5; } 50% { transform: scale(1.15); opacity: 0; } 100% { transform: scale(1); opacity: 0; } }
492
- .vox-widget { animation: vox-fade-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
474
+ return (jsxs(Fragment, { children: [jsx("link", { href: "https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap", rel: "stylesheet" }), jsx("style", { children: `
475
+ @keyframes vox-fade-in { from { opacity: 0; transform: translateY(40px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } }
476
+ @keyframes vox-spin { to { transform: rotate(360deg); } }
477
+ @keyframes vox-ping { 0% { transform: scale(1); opacity: 0.5; } 50% { transform: scale(1.15); opacity: 0; } 100% { transform: scale(1); opacity: 0; } }
478
+ .vox-widget { animation: vox-fade-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
493
479
  ` }), jsxs("div", { style: { position: 'fixed', zIndex: 10000, display: 'flex', flexDirection: 'column', alignItems: finalPosition === 'bottom-left' ? 'flex-start' : 'flex-end', gap: '24px', ...positionStyles }, children: [isOpen && (jsxs("div", { className: "vox-widget", style: {
494
480
  width: 'min(calc(100vw - 32px), 360px)', height: '550px', maxHeight: '550px',
495
481
  background: '#000000', overflow: 'hidden', display: 'flex', flexDirection: 'column',
496
- border: '1px solid #3f3f46', boxShadow: '0 20px 40px -10px rgba(0,0,0,0.5)', borderRadius: '12px'
497
- }, children: [jsxs("div", { style: { height: '48px', background: '#000000', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 16px', flexShrink: 0, borderBottom: '1px solid #3f3f46' }, children: [jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [jsx("div", { style: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.3s ease' }, children: finalLogoUrl ? (jsx("img", { src: finalLogoUrl, alt: agentName, style: { width: '28px', height: '28px', objectFit: 'contain', transition: 'opacity 0.3s ease' } }, finalLogoUrl)) : (jsx(MessageCircleIcon, {})) }), jsxs("div", { children: [jsx("h2", { style: { color: '#ffffff', fontWeight: 900, fontSize: '9px', textTransform: 'uppercase', letterSpacing: '0.15em', margin: 0, transition: 'all 0.3s ease' }, children: agentName }), jsxs("p", { style: { color: '#a1a1aa', fontSize: '8px', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', margin: 0, display: 'flex', alignItems: 'center' }, children: [jsx("span", { style: { width: '4px', height: '4px', background: '#ffffff', borderRadius: '50%', marginRight: '6px' } }), "Active"] })] })] }), jsx("button", { onClick: handleClose, style: { background: 'none', border: 'none', color: '#a1a1aa', cursor: 'pointer', padding: '4px', transition: 'color 0.2s' }, children: jsx(XIcon, {}) })] }), jsxs("div", { style: { display: 'flex', padding: '2px', background: '#18181b', borderBottom: '1px solid #3f3f46', flexShrink: 0 }, children: [jsx("button", { onClick: () => handleTabChange('text'), style: {
498
- flex: 1, padding: '8px', fontSize: '9px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.15em',
499
- background: 'none', border: 'none', cursor: 'pointer', color: activeTab === 'text' ? '#ffffff' : '#71717a', transition: 'color 0.2s'
500
- }, children: "Text Interface" }), jsx("div", { style: { width: '1px', background: '#3f3f46' } }), jsx("button", { onClick: () => handleTabChange('voice'), style: {
501
- flex: 1, padding: '8px', fontSize: '9px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '0.15em',
502
- background: 'none', border: 'none', cursor: 'pointer', color: activeTab === 'voice' ? '#ffffff' : '#71717a', transition: 'color 0.2s'
503
- }, children: "Voice Protocol" })] }), jsx("div", { style: { flex: 1, overflow: 'hidden', position: 'relative', background: '#000000' }, children: activeTab === 'text' ? (jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction })) : (isVoiceActive ? (jsx(VoiceInterface, { geminiApiKey: geminiApiKey, voiceName: voiceName, systemInstruction: systemInstruction, onEndCall: handleEndCall })) : (jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction }))) })] })), !isOpen && (!useServerConfig || configLoaded) && (jsx("button", { onClick: handleOpen, style: {
482
+ border: '1px solid #3f3f46', boxShadow: '0 20px 40px -10px rgba(0,0,0,0.5)', borderRadius: '8px',
483
+ transition: 'all 0.3s ease',
484
+ fontFamily: '"Plus Jakarta Sans", sans-serif',
485
+ WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale',
486
+ textAlign: 'left', boxSizing: 'border-box'
487
+ }, children: [jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid #3f3f46', background: '#000000', fontFamily: '"Plus Jakarta Sans", sans-serif' }, children: [jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [jsx("div", { style: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.3s ease', color: '#ffffff' }, children: finalLogoUrl ? (jsx("img", { src: finalLogoUrl, alt: agentName, style: { width: '28px', height: '28px', objectFit: 'contain', transition: 'opacity 0.3s ease' } }, finalLogoUrl)) : (activeTab === 'voice' ? jsx(MicIcon, {}) : jsx(MessageCircleIcon, {})) }), jsxs("div", { children: [jsx("h2", { style: { color: '#ffffff', fontWeight: 600, fontSize: '12px', margin: 0, transition: 'all 0.3s ease', lineHeight: '1.2', fontFamily: 'inherit' }, children: agentName }), jsxs("p", { style: { color: '#a1a1aa', fontSize: '11px', fontWeight: 400, margin: 0, display: 'flex', alignItems: 'center', lineHeight: '1.2', fontFamily: 'inherit' }, children: [jsx("span", { style: { width: '6px', height: '6px', background: '#ffffff', borderRadius: '50%', marginRight: '6px' } }), "Active"] })] })] }), jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '2px', padding: '2px', borderRadius: '8px', background: '#18181b' }, children: [jsx("button", { onClick: () => handleTabChange('text'), style: {
488
+ padding: '6px 12px', fontSize: '11px', fontWeight: 500, borderRadius: '6px',
489
+ background: activeTab === 'text' ? '#27272a' : 'transparent', border: 'none', cursor: 'pointer', color: activeTab === 'text' ? '#ffffff' : '#71717a', transition: 'all 0.2s',
490
+ boxShadow: activeTab === 'text' ? '0 1px 2px rgba(0,0,0,0.1)' : 'none'
491
+ }, children: "Text" }), jsx("button", { onClick: () => handleTabChange('voice'), style: {
492
+ padding: '6px 12px', fontSize: '11px', fontWeight: 500, borderRadius: '6px',
493
+ background: activeTab === 'voice' ? '#27272a' : 'transparent', border: 'none', cursor: 'pointer', color: activeTab === 'voice' ? '#ffffff' : '#71717a', transition: 'all 0.2s',
494
+ boxShadow: activeTab === 'voice' ? '0 1px 2px rgba(0,0,0,0.1)' : 'none'
495
+ }, children: "Voice" })] }), jsx("button", { onClick: handleClose, style: { background: 'none', border: 'none', color: '#a1a1aa', cursor: 'pointer', padding: '4px', transition: 'color 0.2s' }, children: jsx(XIcon, {}) })] }), jsx("div", { style: { flex: 1, overflow: 'hidden', position: 'relative', background: '#000000' }, children: activeTab === 'text' ? (jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction, logoUrl: finalLogoUrl })) : (isVoiceActive ? (jsx(VoiceInterface, { geminiApiKey: geminiApiKey || serverGeminiApiKey || '', voiceName: voiceName, systemInstruction: systemInstruction, onEndCall: handleEndCall })) : (jsx(TextInterface, { apiKey: apiKey, apiUrl: apiUrl, agentName: agentName, systemInstruction: systemInstruction, logoUrl: finalLogoUrl }))) })] })), !isOpen && (!useServerConfig || configLoaded) && (jsx("button", { onClick: handleOpen, style: {
504
496
  width: `${finalButtonSize}px`, height: `${finalButtonSize}px`,
505
497
  borderRadius: `${finalBorderRadius}px`,
506
498
  background: finalButtonColor, color: finalIconColor, border: 'none', cursor: 'pointer',