@makemore/agent-frontend 1.4.0 → 1.5.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 +28 -0
- package/dist/chat-widget.css +89 -0
- package/dist/chat-widget.js +103 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -165,6 +165,12 @@ See `django-tts-example.py` for the complete Django backend implementation.
|
|
|
165
165
|
| `ttsVoices` | object | `{ assistant: null, user: null }` | Voice IDs (only if not using proxy) |
|
|
166
166
|
| `ttsModel` | string | `'eleven_turbo_v2_5'` | ElevenLabs model (only if not using proxy) |
|
|
167
167
|
| `ttsSettings` | object | See below | ElevenLabs voice settings (only if not using proxy) |
|
|
168
|
+
| `availableVoices` | array | `[]` | List of available voices (auto-populated from ElevenLabs API) |
|
|
169
|
+
| `showClearButton` | boolean | `true` | Show clear conversation button in header |
|
|
170
|
+
| `showDebugButton` | boolean | `true` | Show debug mode toggle button in header |
|
|
171
|
+
| `showTTSButton` | boolean | `true` | Show TTS toggle button in header |
|
|
172
|
+
| `showVoiceSettings` | boolean | `true` | Show voice settings button in header (direct API only) |
|
|
173
|
+
| `showExpandButton` | boolean | `true` | Show expand/minimize button in header |
|
|
168
174
|
|
|
169
175
|
### Text-to-Speech (ElevenLabs)
|
|
170
176
|
|
|
@@ -237,6 +243,26 @@ ChatWidget.init({
|
|
|
237
243
|
```javascript
|
|
238
244
|
ChatWidget.toggleTTS(); // Toggle on/off
|
|
239
245
|
ChatWidget.stopSpeech(); // Stop current speech and clear queue
|
|
246
|
+
ChatWidget.setVoice('assistant', 'voice_id'); // Change assistant voice
|
|
247
|
+
ChatWidget.setVoice('user', 'voice_id'); // Change user voice
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Voice Settings UI:**
|
|
251
|
+
|
|
252
|
+
When using direct API mode (not proxy), a voice settings button (🎙️) appears in the header. Click it to:
|
|
253
|
+
- Select assistant voice from dropdown
|
|
254
|
+
- Select customer voice for demo mode
|
|
255
|
+
- Voices are automatically fetched from your ElevenLabs account
|
|
256
|
+
|
|
257
|
+
**Customize Header Buttons:**
|
|
258
|
+
```javascript
|
|
259
|
+
ChatWidget.init({
|
|
260
|
+
showClearButton: true, // Show/hide clear button
|
|
261
|
+
showDebugButton: true, // Show/hide debug button
|
|
262
|
+
showTTSButton: true, // Show/hide TTS toggle
|
|
263
|
+
showVoiceSettings: true, // Show/hide voice settings (direct API only)
|
|
264
|
+
showExpandButton: true, // Show/hide expand button
|
|
265
|
+
});
|
|
240
266
|
```
|
|
241
267
|
|
|
242
268
|
### Demo Flow Control
|
|
@@ -335,6 +361,8 @@ ChatWidget.clearMessages();
|
|
|
335
361
|
// Text-to-speech controls
|
|
336
362
|
ChatWidget.toggleTTS(); // Toggle TTS on/off
|
|
337
363
|
ChatWidget.stopSpeech(); // Stop current speech and clear queue
|
|
364
|
+
ChatWidget.setVoice('assistant', 'voice_id'); // Change assistant voice
|
|
365
|
+
ChatWidget.setVoice('user', 'voice_id'); // Change user voice
|
|
338
366
|
|
|
339
367
|
// Start a demo flow
|
|
340
368
|
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
|
@@ -61,6 +61,13 @@
|
|
|
61
61
|
style: 0.0,
|
|
62
62
|
use_speaker_boost: true,
|
|
63
63
|
},
|
|
64
|
+
availableVoices: [], // List of available voices for UI dropdown
|
|
65
|
+
// UI visibility controls
|
|
66
|
+
showClearButton: true,
|
|
67
|
+
showDebugButton: true,
|
|
68
|
+
showTTSButton: true,
|
|
69
|
+
showVoiceSettings: true,
|
|
70
|
+
showExpandButton: true,
|
|
64
71
|
};
|
|
65
72
|
|
|
66
73
|
// State
|
|
@@ -82,6 +89,7 @@
|
|
|
82
89
|
currentAudio: null,
|
|
83
90
|
isSpeaking: false,
|
|
84
91
|
speechQueue: [],
|
|
92
|
+
voiceSettingsOpen: false,
|
|
85
93
|
};
|
|
86
94
|
|
|
87
95
|
// DOM elements
|
|
@@ -285,6 +293,35 @@
|
|
|
285
293
|
render();
|
|
286
294
|
}
|
|
287
295
|
|
|
296
|
+
function toggleVoiceSettings() {
|
|
297
|
+
state.voiceSettingsOpen = !state.voiceSettingsOpen;
|
|
298
|
+
render();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function setVoice(role, voiceId) {
|
|
302
|
+
config.ttsVoices[role] = voiceId;
|
|
303
|
+
render();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function fetchAvailableVoices() {
|
|
307
|
+
if (!config.elevenLabsApiKey) return;
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const response = await fetch('https://api.elevenlabs.io/v1/voices', {
|
|
311
|
+
headers: {
|
|
312
|
+
'xi-api-key': config.elevenLabsApiKey,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
if (response.ok) {
|
|
317
|
+
const data = await response.json();
|
|
318
|
+
config.availableVoices = data.voices || [];
|
|
319
|
+
}
|
|
320
|
+
} catch (err) {
|
|
321
|
+
console.error('[ChatWidget] Failed to fetch voices:', err);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
288
325
|
// ============================================================================
|
|
289
326
|
// Session Management
|
|
290
327
|
// ============================================================================
|
|
@@ -694,6 +731,44 @@
|
|
|
694
731
|
`;
|
|
695
732
|
}
|
|
696
733
|
|
|
734
|
+
function renderVoiceSettings() {
|
|
735
|
+
if (!state.voiceSettingsOpen) return '';
|
|
736
|
+
|
|
737
|
+
const voiceOptions = (role) => {
|
|
738
|
+
if (config.availableVoices.length === 0) {
|
|
739
|
+
return '<option value="">Loading voices...</option>';
|
|
740
|
+
}
|
|
741
|
+
return config.availableVoices.map(voice => `
|
|
742
|
+
<option value="${voice.voice_id}" ${config.ttsVoices[role] === voice.voice_id ? 'selected' : ''}>
|
|
743
|
+
${escapeHtml(voice.name)}
|
|
744
|
+
</option>
|
|
745
|
+
`).join('');
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
return `
|
|
749
|
+
<div class="cw-voice-settings">
|
|
750
|
+
<div class="cw-voice-settings-header">
|
|
751
|
+
<span>🎙️ Voice Settings</span>
|
|
752
|
+
<button class="cw-voice-settings-close" data-action="toggle-voice-settings">✕</button>
|
|
753
|
+
</div>
|
|
754
|
+
<div class="cw-voice-settings-content">
|
|
755
|
+
<div class="cw-voice-setting">
|
|
756
|
+
<label>Assistant Voice</label>
|
|
757
|
+
<select class="cw-voice-select" data-role="assistant" onchange="ChatWidget.setVoice('assistant', this.value)">
|
|
758
|
+
${voiceOptions('assistant')}
|
|
759
|
+
</select>
|
|
760
|
+
</div>
|
|
761
|
+
<div class="cw-voice-setting">
|
|
762
|
+
<label>Customer Voice (Demo)</label>
|
|
763
|
+
<select class="cw-voice-select" data-role="user" onchange="ChatWidget.setVoice('user', this.value)">
|
|
764
|
+
${voiceOptions('user')}
|
|
765
|
+
</select>
|
|
766
|
+
</div>
|
|
767
|
+
</div>
|
|
768
|
+
</div>
|
|
769
|
+
`;
|
|
770
|
+
}
|
|
771
|
+
|
|
697
772
|
function renderJourneyDropdown() {
|
|
698
773
|
if (!config.enableAutoRun || Object.keys(config.journeyTypes).length === 0) {
|
|
699
774
|
return '';
|
|
@@ -824,13 +899,15 @@
|
|
|
824
899
|
<div class="cw-header" style="background-color: ${config.primaryColor}">
|
|
825
900
|
<span class="cw-title">${escapeHtml(config.title)}</span>
|
|
826
901
|
<div class="cw-header-actions">
|
|
827
|
-
|
|
828
|
-
<
|
|
829
|
-
<
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
902
|
+
${config.showClearButton ? `
|
|
903
|
+
<button class="cw-header-btn" data-action="clear" title="Clear Conversation" ${state.isLoading || state.messages.length === 0 ? 'disabled' : ''}>
|
|
904
|
+
<svg class="cw-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
905
|
+
<polyline points="3 6 5 6 21 6"></polyline>
|
|
906
|
+
<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>
|
|
907
|
+
</svg>
|
|
908
|
+
</button>
|
|
909
|
+
` : ''}
|
|
910
|
+
${config.showDebugButton && config.enableDebugMode ? `
|
|
834
911
|
<button class="cw-header-btn ${state.debugMode ? 'cw-btn-active' : ''}" data-action="toggle-debug" title="${state.debugMode ? 'Hide Debug Info' : 'Show Debug Info'}">
|
|
835
912
|
<svg class="cw-icon-sm ${state.debugMode ? 'cw-icon-warning' : ''}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
836
913
|
<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 +915,30 @@
|
|
|
838
915
|
</svg>
|
|
839
916
|
</button>
|
|
840
917
|
` : ''}
|
|
841
|
-
${config.elevenLabsApiKey ? `
|
|
918
|
+
${config.showTTSButton && (config.elevenLabsApiKey || config.ttsProxyUrl) ? `
|
|
842
919
|
<button class="cw-header-btn ${config.enableTTS ? 'cw-btn-active' : ''} ${state.isSpeaking ? 'cw-btn-speaking' : ''}"
|
|
843
920
|
data-action="toggle-tts"
|
|
844
921
|
title="${config.enableTTS ? (state.isSpeaking ? 'Speaking...' : 'TTS Enabled') : 'TTS Disabled'}">
|
|
845
922
|
${state.isSpeaking ? '🔊' : (config.enableTTS ? '🔉' : '🔇')}
|
|
846
923
|
</button>
|
|
847
924
|
` : ''}
|
|
925
|
+
${config.showVoiceSettings && config.elevenLabsApiKey && !config.ttsProxyUrl ? `
|
|
926
|
+
<button class="cw-header-btn ${state.voiceSettingsOpen ? 'cw-btn-active' : ''}" data-action="toggle-voice-settings" title="Voice Settings">
|
|
927
|
+
🎙️
|
|
928
|
+
</button>
|
|
929
|
+
` : ''}
|
|
848
930
|
${renderJourneyDropdown()}
|
|
849
|
-
|
|
850
|
-
${state.isExpanded ? '
|
|
851
|
-
|
|
931
|
+
${config.showExpandButton ? `
|
|
932
|
+
<button class="cw-header-btn" data-action="toggle-expand" title="${state.isExpanded ? 'Minimize' : 'Expand'}">
|
|
933
|
+
${state.isExpanded ? '⊖' : '⊕'}
|
|
934
|
+
</button>
|
|
935
|
+
` : ''}
|
|
852
936
|
<button class="cw-header-btn" data-action="close" title="Close">
|
|
853
937
|
✕
|
|
854
938
|
</button>
|
|
855
939
|
</div>
|
|
856
940
|
</div>
|
|
941
|
+
${renderVoiceSettings()}
|
|
857
942
|
${statusBar}
|
|
858
943
|
<div class="cw-messages" id="cw-messages">
|
|
859
944
|
${messagesHtml}
|
|
@@ -893,6 +978,7 @@
|
|
|
893
978
|
case 'toggle-expand': toggleExpand(); break;
|
|
894
979
|
case 'toggle-debug': toggleDebugMode(); break;
|
|
895
980
|
case 'toggle-tts': toggleTTS(); break;
|
|
981
|
+
case 'toggle-voice-settings': toggleVoiceSettings(); break;
|
|
896
982
|
case 'clear': clearMessages(); break;
|
|
897
983
|
case 'stop-autorun': stopAutoRun(); break;
|
|
898
984
|
case 'continue-autorun': continueAutoRun(); break;
|
|
@@ -969,6 +1055,11 @@
|
|
|
969
1055
|
// Initial render
|
|
970
1056
|
render();
|
|
971
1057
|
|
|
1058
|
+
// Fetch available voices if using direct API
|
|
1059
|
+
if (config.elevenLabsApiKey && !config.ttsProxyUrl) {
|
|
1060
|
+
fetchAvailableVoices();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
972
1063
|
console.log('[ChatWidget] Initialized with config:', config);
|
|
973
1064
|
}
|
|
974
1065
|
|
|
@@ -1012,6 +1103,7 @@
|
|
|
1012
1103
|
setAutoRunDelay,
|
|
1013
1104
|
toggleTTS,
|
|
1014
1105
|
stopSpeech,
|
|
1106
|
+
setVoice,
|
|
1015
1107
|
getState: () => ({ ...state }),
|
|
1016
1108
|
getConfig: () => ({ ...config }),
|
|
1017
1109
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makemore/agent-frontend",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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": [
|