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