@makemore/agent-frontend 1.4.0 → 1.6.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/README.md +93 -2
- package/dist/chat-widget.css +89 -0
- package/dist/chat-widget.js +149 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,6 +131,9 @@ See `django-tts-example.py` for the complete Django backend implementation.
|
|
|
131
131
|
anonymousSession: '/api/auth/session/',
|
|
132
132
|
runs: '/api/chat/runs/',
|
|
133
133
|
runEvents: '/api/chat/runs/{runId}/events/',
|
|
134
|
+
simulateCustomer: '/api/chat/simulate-customer/',
|
|
135
|
+
ttsVoices: '/api/tts/voices/', // For voice settings UI (proxy mode)
|
|
136
|
+
ttsSetVoice: '/api/tts/set-voice/', // For voice settings UI (proxy mode)
|
|
134
137
|
},
|
|
135
138
|
});
|
|
136
139
|
</script>
|
|
@@ -165,6 +168,12 @@ See `django-tts-example.py` for the complete Django backend implementation.
|
|
|
165
168
|
| `ttsVoices` | object | `{ assistant: null, user: null }` | Voice IDs (only if not using proxy) |
|
|
166
169
|
| `ttsModel` | string | `'eleven_turbo_v2_5'` | ElevenLabs model (only if not using proxy) |
|
|
167
170
|
| `ttsSettings` | object | See below | ElevenLabs voice settings (only if not using proxy) |
|
|
171
|
+
| `availableVoices` | array | `[]` | List of available voices (auto-populated from ElevenLabs API) |
|
|
172
|
+
| `showClearButton` | boolean | `true` | Show clear conversation button in header |
|
|
173
|
+
| `showDebugButton` | boolean | `true` | Show debug mode toggle button in header |
|
|
174
|
+
| `showTTSButton` | boolean | `true` | Show TTS toggle button in header |
|
|
175
|
+
| `showVoiceSettings` | boolean | `true` | Show voice settings button in header (works with proxy and direct API) |
|
|
176
|
+
| `showExpandButton` | boolean | `true` | Show expand/minimize button in header |
|
|
168
177
|
|
|
169
178
|
### Text-to-Speech (ElevenLabs)
|
|
170
179
|
|
|
@@ -195,8 +204,66 @@ ELEVENLABS_VOICES = {
|
|
|
195
204
|
'user': 'pNInz6obpgDQGcFmaJgB', # Adam
|
|
196
205
|
}
|
|
197
206
|
```
|
|
198
|
-
3. Add
|
|
199
|
-
4. Add URL
|
|
207
|
+
3. Add views from `django-tts-example.py` to your Django app
|
|
208
|
+
4. Add URL routes:
|
|
209
|
+
```python
|
|
210
|
+
path('api/tts/speak/', views.text_to_speech),
|
|
211
|
+
path('api/tts/voices/', views.get_voices), # For voice settings UI
|
|
212
|
+
path('api/tts/set-voice/', views.set_voice), # For voice settings UI
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Voice Settings Support:**
|
|
216
|
+
|
|
217
|
+
The widget now supports voice settings UI in proxy mode! Add these endpoints to enable the voice picker:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
# Get available voices
|
|
221
|
+
@api_view(['GET'])
|
|
222
|
+
def get_voices(request):
|
|
223
|
+
"""Fetch available voices from ElevenLabs"""
|
|
224
|
+
try:
|
|
225
|
+
response = requests.get(
|
|
226
|
+
'https://api.elevenlabs.io/v1/voices',
|
|
227
|
+
headers={'xi-api-key': settings.ELEVENLABS_API_KEY}
|
|
228
|
+
)
|
|
229
|
+
return JsonResponse(response.json())
|
|
230
|
+
except Exception as e:
|
|
231
|
+
return JsonResponse({'error': str(e)}, status=500)
|
|
232
|
+
|
|
233
|
+
# Set voice for user session
|
|
234
|
+
@api_view(['POST'])
|
|
235
|
+
def set_voice(request):
|
|
236
|
+
"""Update voice preference for user's session"""
|
|
237
|
+
role = request.data.get('role') # 'assistant' or 'user'
|
|
238
|
+
voice_id = request.data.get('voice_id')
|
|
239
|
+
|
|
240
|
+
# Store in session or database
|
|
241
|
+
if not hasattr(request, 'session'):
|
|
242
|
+
return JsonResponse({'error': 'Session not available'}, status=400)
|
|
243
|
+
|
|
244
|
+
if role not in ['assistant', 'user']:
|
|
245
|
+
return JsonResponse({'error': 'Invalid role'}, status=400)
|
|
246
|
+
|
|
247
|
+
# Store voice preference in session
|
|
248
|
+
if 'tts_voices' not in request.session:
|
|
249
|
+
request.session['tts_voices'] = {}
|
|
250
|
+
request.session['tts_voices'][role] = voice_id
|
|
251
|
+
request.session.modified = True
|
|
252
|
+
|
|
253
|
+
return JsonResponse({'success': True, 'role': role, 'voice_id': voice_id})
|
|
254
|
+
|
|
255
|
+
# Update text_to_speech view to use session voices
|
|
256
|
+
@api_view(['POST'])
|
|
257
|
+
def text_to_speech(request):
|
|
258
|
+
text = request.data.get('text', '')
|
|
259
|
+
role = request.data.get('role', 'assistant')
|
|
260
|
+
|
|
261
|
+
# Get voice from session or fall back to settings
|
|
262
|
+
session_voices = request.session.get('tts_voices', {})
|
|
263
|
+
voice_id = session_voices.get(role) or settings.ELEVENLABS_VOICES.get(role)
|
|
264
|
+
|
|
265
|
+
# ... rest of TTS implementation
|
|
266
|
+
```
|
|
200
267
|
|
|
201
268
|
#### Option 2: Direct API (Client-Side)
|
|
202
269
|
|
|
@@ -237,6 +304,28 @@ ChatWidget.init({
|
|
|
237
304
|
```javascript
|
|
238
305
|
ChatWidget.toggleTTS(); // Toggle on/off
|
|
239
306
|
ChatWidget.stopSpeech(); // Stop current speech and clear queue
|
|
307
|
+
ChatWidget.setVoice('assistant', 'voice_id'); // Change assistant voice
|
|
308
|
+
ChatWidget.setVoice('user', 'voice_id'); // Change user voice
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Voice Settings UI:**
|
|
312
|
+
|
|
313
|
+
A voice settings button (🎙️) appears in the header when TTS is enabled. Click it to:
|
|
314
|
+
- Select assistant voice from dropdown
|
|
315
|
+
- Select customer voice for demo mode
|
|
316
|
+
- Voices are automatically fetched from your ElevenLabs account (direct API) or Django backend (proxy mode)
|
|
317
|
+
|
|
318
|
+
**Works with both proxy and direct API modes!** Just implement the `/api/tts/voices/` and `/api/tts/set-voice/` endpoints in your Django backend (see above).
|
|
319
|
+
|
|
320
|
+
**Customize Header Buttons:**
|
|
321
|
+
```javascript
|
|
322
|
+
ChatWidget.init({
|
|
323
|
+
showClearButton: true, // Show/hide clear button
|
|
324
|
+
showDebugButton: true, // Show/hide debug button
|
|
325
|
+
showTTSButton: true, // Show/hide TTS toggle
|
|
326
|
+
showVoiceSettings: true, // Show/hide voice settings (direct API only)
|
|
327
|
+
showExpandButton: true, // Show/hide expand button
|
|
328
|
+
});
|
|
240
329
|
```
|
|
241
330
|
|
|
242
331
|
### Demo Flow Control
|
|
@@ -335,6 +424,8 @@ ChatWidget.clearMessages();
|
|
|
335
424
|
// Text-to-speech controls
|
|
336
425
|
ChatWidget.toggleTTS(); // Toggle TTS on/off
|
|
337
426
|
ChatWidget.stopSpeech(); // Stop current speech and clear queue
|
|
427
|
+
ChatWidget.setVoice('assistant', 'voice_id'); // Change assistant voice
|
|
428
|
+
ChatWidget.setVoice('user', 'voice_id'); // Change user voice
|
|
338
429
|
|
|
339
430
|
// Start a demo flow
|
|
340
431
|
ChatWidget.startDemoFlow('quote');
|
package/dist/chat-widget.css
CHANGED
|
@@ -172,6 +172,95 @@
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/* Voice Settings */
|
|
176
|
+
.cw-voice-settings {
|
|
177
|
+
background: var(--cw-bg-muted);
|
|
178
|
+
border-bottom: 1px solid var(--cw-border);
|
|
179
|
+
animation: slideDown 0.2s ease-out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@keyframes slideDown {
|
|
183
|
+
from {
|
|
184
|
+
max-height: 0;
|
|
185
|
+
opacity: 0;
|
|
186
|
+
}
|
|
187
|
+
to {
|
|
188
|
+
max-height: 200px;
|
|
189
|
+
opacity: 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.cw-voice-settings-header {
|
|
194
|
+
display: flex;
|
|
195
|
+
justify-content: space-between;
|
|
196
|
+
align-items: center;
|
|
197
|
+
padding: 8px 16px;
|
|
198
|
+
font-size: 13px;
|
|
199
|
+
font-weight: 600;
|
|
200
|
+
color: var(--cw-text);
|
|
201
|
+
border-bottom: 1px solid var(--cw-border);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.cw-voice-settings-close {
|
|
205
|
+
all: initial;
|
|
206
|
+
font-family: inherit;
|
|
207
|
+
background: none;
|
|
208
|
+
border: none;
|
|
209
|
+
color: var(--cw-text-muted);
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
font-size: 16px;
|
|
212
|
+
padding: 4px;
|
|
213
|
+
line-height: 1;
|
|
214
|
+
transition: color 0.15s;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.cw-voice-settings-close:hover {
|
|
218
|
+
color: var(--cw-text);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.cw-voice-settings-content {
|
|
222
|
+
padding: 12px 16px;
|
|
223
|
+
display: flex;
|
|
224
|
+
flex-direction: column;
|
|
225
|
+
gap: 12px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.cw-voice-setting {
|
|
229
|
+
display: flex;
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
gap: 4px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.cw-voice-setting label {
|
|
235
|
+
font-size: 12px;
|
|
236
|
+
font-weight: 500;
|
|
237
|
+
color: var(--cw-text-muted);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.cw-voice-select {
|
|
241
|
+
all: initial;
|
|
242
|
+
font-family: inherit;
|
|
243
|
+
width: 100%;
|
|
244
|
+
padding: 8px 12px;
|
|
245
|
+
border: 1px solid var(--cw-border);
|
|
246
|
+
border-radius: 6px;
|
|
247
|
+
background: var(--cw-bg);
|
|
248
|
+
color: var(--cw-text);
|
|
249
|
+
font-size: 13px;
|
|
250
|
+
cursor: pointer;
|
|
251
|
+
transition: border-color 0.15s;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.cw-voice-select:hover {
|
|
255
|
+
border-color: var(--cw-primary);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.cw-voice-select:focus {
|
|
259
|
+
outline: none;
|
|
260
|
+
border-color: var(--cw-primary);
|
|
261
|
+
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
|
|
262
|
+
}
|
|
263
|
+
|
|
175
264
|
/* Status bar */
|
|
176
265
|
.cw-status-bar {
|
|
177
266
|
display: flex;
|
package/dist/chat-widget.js
CHANGED
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
runs: '/api/agent-runtime/runs/',
|
|
43
43
|
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
44
44
|
simulateCustomer: '/api/agent-runtime/simulate-customer/',
|
|
45
|
+
ttsVoices: '/api/tts/voices/', // For fetching available voices (proxy mode)
|
|
46
|
+
ttsSetVoice: '/api/tts/set-voice/', // For setting voice (proxy mode)
|
|
45
47
|
},
|
|
46
48
|
// Demo flow control
|
|
47
49
|
autoRunDelay: 1000, // Delay in ms before auto-generating next message
|
|
@@ -61,6 +63,13 @@
|
|
|
61
63
|
style: 0.0,
|
|
62
64
|
use_speaker_boost: true,
|
|
63
65
|
},
|
|
66
|
+
availableVoices: [], // List of available voices for UI dropdown
|
|
67
|
+
// UI visibility controls
|
|
68
|
+
showClearButton: true,
|
|
69
|
+
showDebugButton: true,
|
|
70
|
+
showTTSButton: true,
|
|
71
|
+
showVoiceSettings: true,
|
|
72
|
+
showExpandButton: true,
|
|
64
73
|
};
|
|
65
74
|
|
|
66
75
|
// State
|
|
@@ -82,6 +91,7 @@
|
|
|
82
91
|
currentAudio: null,
|
|
83
92
|
isSpeaking: false,
|
|
84
93
|
speechQueue: [],
|
|
94
|
+
voiceSettingsOpen: false,
|
|
85
95
|
};
|
|
86
96
|
|
|
87
97
|
// DOM elements
|
|
@@ -285,6 +295,79 @@
|
|
|
285
295
|
render();
|
|
286
296
|
}
|
|
287
297
|
|
|
298
|
+
function toggleVoiceSettings() {
|
|
299
|
+
state.voiceSettingsOpen = !state.voiceSettingsOpen;
|
|
300
|
+
render();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function setVoice(role, voiceId) {
|
|
304
|
+
config.ttsVoices[role] = voiceId;
|
|
305
|
+
|
|
306
|
+
// If using proxy, notify backend of voice change
|
|
307
|
+
if (config.ttsProxyUrl) {
|
|
308
|
+
try {
|
|
309
|
+
const token = await getOrCreateSession();
|
|
310
|
+
const headers = {
|
|
311
|
+
'Content-Type': 'application/json',
|
|
312
|
+
};
|
|
313
|
+
if (token) {
|
|
314
|
+
headers[config.anonymousTokenHeader] = token;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
await fetch(`${config.backendUrl}${config.apiPaths.ttsSetVoice}`, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers,
|
|
320
|
+
body: JSON.stringify({ role, voice_id: voiceId }),
|
|
321
|
+
});
|
|
322
|
+
} catch (err) {
|
|
323
|
+
console.error('[ChatWidget] Failed to set voice on backend:', err);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
render();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async function fetchAvailableVoices() {
|
|
331
|
+
try {
|
|
332
|
+
let voices = [];
|
|
333
|
+
|
|
334
|
+
if (config.ttsProxyUrl) {
|
|
335
|
+
// Fetch voices from Django backend
|
|
336
|
+
const token = await getOrCreateSession();
|
|
337
|
+
const headers = {};
|
|
338
|
+
if (token) {
|
|
339
|
+
headers[config.anonymousTokenHeader] = token;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const response = await fetch(`${config.backendUrl}${config.apiPaths.ttsVoices}`, {
|
|
343
|
+
headers,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
if (response.ok) {
|
|
347
|
+
const data = await response.json();
|
|
348
|
+
voices = data.voices || [];
|
|
349
|
+
}
|
|
350
|
+
} else if (config.elevenLabsApiKey) {
|
|
351
|
+
// Fetch voices directly from ElevenLabs
|
|
352
|
+
const response = await fetch('https://api.elevenlabs.io/v1/voices', {
|
|
353
|
+
headers: {
|
|
354
|
+
'xi-api-key': config.elevenLabsApiKey,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (response.ok) {
|
|
359
|
+
const data = await response.json();
|
|
360
|
+
voices = data.voices || [];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
config.availableVoices = voices;
|
|
365
|
+
render(); // Re-render to update dropdowns
|
|
366
|
+
} catch (err) {
|
|
367
|
+
console.error('[ChatWidget] Failed to fetch voices:', err);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
288
371
|
// ============================================================================
|
|
289
372
|
// Session Management
|
|
290
373
|
// ============================================================================
|
|
@@ -694,6 +777,44 @@
|
|
|
694
777
|
`;
|
|
695
778
|
}
|
|
696
779
|
|
|
780
|
+
function renderVoiceSettings() {
|
|
781
|
+
if (!state.voiceSettingsOpen) return '';
|
|
782
|
+
|
|
783
|
+
const voiceOptions = (role) => {
|
|
784
|
+
if (config.availableVoices.length === 0) {
|
|
785
|
+
return '<option value="">Loading voices...</option>';
|
|
786
|
+
}
|
|
787
|
+
return config.availableVoices.map(voice => `
|
|
788
|
+
<option value="${voice.voice_id}" ${config.ttsVoices[role] === voice.voice_id ? 'selected' : ''}>
|
|
789
|
+
${escapeHtml(voice.name)}
|
|
790
|
+
</option>
|
|
791
|
+
`).join('');
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
return `
|
|
795
|
+
<div class="cw-voice-settings">
|
|
796
|
+
<div class="cw-voice-settings-header">
|
|
797
|
+
<span>🎙️ Voice Settings</span>
|
|
798
|
+
<button class="cw-voice-settings-close" data-action="toggle-voice-settings">✕</button>
|
|
799
|
+
</div>
|
|
800
|
+
<div class="cw-voice-settings-content">
|
|
801
|
+
<div class="cw-voice-setting">
|
|
802
|
+
<label>Assistant Voice</label>
|
|
803
|
+
<select class="cw-voice-select" data-role="assistant" onchange="ChatWidget.setVoice('assistant', this.value)">
|
|
804
|
+
${voiceOptions('assistant')}
|
|
805
|
+
</select>
|
|
806
|
+
</div>
|
|
807
|
+
<div class="cw-voice-setting">
|
|
808
|
+
<label>Customer Voice (Demo)</label>
|
|
809
|
+
<select class="cw-voice-select" data-role="user" onchange="ChatWidget.setVoice('user', this.value)">
|
|
810
|
+
${voiceOptions('user')}
|
|
811
|
+
</select>
|
|
812
|
+
</div>
|
|
813
|
+
</div>
|
|
814
|
+
</div>
|
|
815
|
+
`;
|
|
816
|
+
}
|
|
817
|
+
|
|
697
818
|
function renderJourneyDropdown() {
|
|
698
819
|
if (!config.enableAutoRun || Object.keys(config.journeyTypes).length === 0) {
|
|
699
820
|
return '';
|
|
@@ -824,13 +945,15 @@
|
|
|
824
945
|
<div class="cw-header" style="background-color: ${config.primaryColor}">
|
|
825
946
|
<span class="cw-title">${escapeHtml(config.title)}</span>
|
|
826
947
|
<div class="cw-header-actions">
|
|
827
|
-
|
|
828
|
-
<
|
|
829
|
-
<
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
948
|
+
${config.showClearButton ? `
|
|
949
|
+
<button class="cw-header-btn" data-action="clear" title="Clear Conversation" ${state.isLoading || state.messages.length === 0 ? 'disabled' : ''}>
|
|
950
|
+
<svg class="cw-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
951
|
+
<polyline points="3 6 5 6 21 6"></polyline>
|
|
952
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
953
|
+
</svg>
|
|
954
|
+
</button>
|
|
955
|
+
` : ''}
|
|
956
|
+
${config.showDebugButton && config.enableDebugMode ? `
|
|
834
957
|
<button class="cw-header-btn ${state.debugMode ? 'cw-btn-active' : ''}" data-action="toggle-debug" title="${state.debugMode ? 'Hide Debug Info' : 'Show Debug Info'}">
|
|
835
958
|
<svg class="cw-icon-sm ${state.debugMode ? 'cw-icon-warning' : ''}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
836
959
|
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 18a8 8 0 1 1 8-8 8 8 0 0 1-8 8z"></path>
|
|
@@ -838,22 +961,30 @@
|
|
|
838
961
|
</svg>
|
|
839
962
|
</button>
|
|
840
963
|
` : ''}
|
|
841
|
-
${config.elevenLabsApiKey ? `
|
|
964
|
+
${config.showTTSButton && (config.elevenLabsApiKey || config.ttsProxyUrl) ? `
|
|
842
965
|
<button class="cw-header-btn ${config.enableTTS ? 'cw-btn-active' : ''} ${state.isSpeaking ? 'cw-btn-speaking' : ''}"
|
|
843
966
|
data-action="toggle-tts"
|
|
844
967
|
title="${config.enableTTS ? (state.isSpeaking ? 'Speaking...' : 'TTS Enabled') : 'TTS Disabled'}">
|
|
845
968
|
${state.isSpeaking ? '🔊' : (config.enableTTS ? '🔉' : '🔇')}
|
|
846
969
|
</button>
|
|
847
970
|
` : ''}
|
|
971
|
+
${config.showVoiceSettings && (config.elevenLabsApiKey || config.ttsProxyUrl) ? `
|
|
972
|
+
<button class="cw-header-btn ${state.voiceSettingsOpen ? 'cw-btn-active' : ''}" data-action="toggle-voice-settings" title="Voice Settings">
|
|
973
|
+
🎙️
|
|
974
|
+
</button>
|
|
975
|
+
` : ''}
|
|
848
976
|
${renderJourneyDropdown()}
|
|
849
|
-
|
|
850
|
-
${state.isExpanded ? '
|
|
851
|
-
|
|
977
|
+
${config.showExpandButton ? `
|
|
978
|
+
<button class="cw-header-btn" data-action="toggle-expand" title="${state.isExpanded ? 'Minimize' : 'Expand'}">
|
|
979
|
+
${state.isExpanded ? '⊖' : '⊕'}
|
|
980
|
+
</button>
|
|
981
|
+
` : ''}
|
|
852
982
|
<button class="cw-header-btn" data-action="close" title="Close">
|
|
853
983
|
✕
|
|
854
984
|
</button>
|
|
855
985
|
</div>
|
|
856
986
|
</div>
|
|
987
|
+
${renderVoiceSettings()}
|
|
857
988
|
${statusBar}
|
|
858
989
|
<div class="cw-messages" id="cw-messages">
|
|
859
990
|
${messagesHtml}
|
|
@@ -893,6 +1024,7 @@
|
|
|
893
1024
|
case 'toggle-expand': toggleExpand(); break;
|
|
894
1025
|
case 'toggle-debug': toggleDebugMode(); break;
|
|
895
1026
|
case 'toggle-tts': toggleTTS(); break;
|
|
1027
|
+
case 'toggle-voice-settings': toggleVoiceSettings(); break;
|
|
896
1028
|
case 'clear': clearMessages(); break;
|
|
897
1029
|
case 'stop-autorun': stopAutoRun(); break;
|
|
898
1030
|
case 'continue-autorun': continueAutoRun(); break;
|
|
@@ -969,6 +1101,11 @@
|
|
|
969
1101
|
// Initial render
|
|
970
1102
|
render();
|
|
971
1103
|
|
|
1104
|
+
// Fetch available voices if TTS is configured
|
|
1105
|
+
if (config.elevenLabsApiKey || config.ttsProxyUrl) {
|
|
1106
|
+
fetchAvailableVoices();
|
|
1107
|
+
}
|
|
1108
|
+
|
|
972
1109
|
console.log('[ChatWidget] Initialized with config:', config);
|
|
973
1110
|
}
|
|
974
1111
|
|
|
@@ -1012,6 +1149,7 @@
|
|
|
1012
1149
|
setAutoRunDelay,
|
|
1013
1150
|
toggleTTS,
|
|
1014
1151
|
stopSpeech,
|
|
1152
|
+
setVoice,
|
|
1015
1153
|
getState: () => ({ ...state }),
|
|
1016
1154
|
getConfig: () => ({ ...config }),
|
|
1017
1155
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makemore/agent-frontend",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A standalone, zero-dependency chat widget for AI agents. Embed conversational AI into any website with a single script tag.",
|
|
5
5
|
"main": "dist/chat-widget.js",
|
|
6
6
|
"files": [
|