@unctad-ai/voice-agent-ui 3.0.3 → 5.0.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/{VoiceSettingsView-25CXZPWY.js → VoiceSettingsView-QY7TEHYZ.js} +2 -2
- package/dist/{chunk-MUM5DNV4.js → chunk-K3JJWWRQ.js} +360 -41
- package/dist/chunk-K3JJWWRQ.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +104 -75
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-MUM5DNV4.js.map +0 -1
- /package/dist/{VoiceSettingsView-25CXZPWY.js.map → VoiceSettingsView-QY7TEHYZ.js.map} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
SliderSetting,
|
|
6
6
|
ToggleSetting,
|
|
7
7
|
VoiceSettingsView
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-K3JJWWRQ.js";
|
|
9
9
|
export {
|
|
10
10
|
Divider,
|
|
11
11
|
SelectSetting,
|
|
@@ -14,4 +14,4 @@ export {
|
|
|
14
14
|
ToggleSetting,
|
|
15
15
|
VoiceSettingsView as default
|
|
16
16
|
};
|
|
17
|
-
//# sourceMappingURL=VoiceSettingsView-
|
|
17
|
+
//# sourceMappingURL=VoiceSettingsView-QY7TEHYZ.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/VoiceSettingsView.tsx
|
|
2
|
-
import { useState as useState3 } from "react";
|
|
2
|
+
import { useState as useState3, useCallback as useCallback3 } from "react";
|
|
3
3
|
import { motion, AnimatePresence } from "motion/react";
|
|
4
4
|
import {
|
|
5
5
|
ArrowLeft,
|
|
@@ -152,68 +152,258 @@ function useVoiceSettings() {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// src/components/VoiceSettingsView.tsx
|
|
155
|
-
import { VAD, useSiteConfig as useSiteConfig2 } from "@unctad-ai/voice-agent-core";
|
|
155
|
+
import { VAD, useSiteConfig as useSiteConfig2, usePersonaContext as usePersonaContext2 } from "@unctad-ai/voice-agent-core";
|
|
156
156
|
|
|
157
157
|
// src/components/PersonaSettings.tsx
|
|
158
|
-
import { useState as useState2, useRef as useRef2, useEffect } from "react";
|
|
158
|
+
import { useState as useState2, useRef as useRef2, useEffect, useCallback as useCallback2 } from "react";
|
|
159
159
|
import { usePersonaContext, useSiteConfig } from "@unctad-ai/voice-agent-core";
|
|
160
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
161
|
-
|
|
160
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
161
|
+
var LANGUAGE_OPTIONS = [
|
|
162
|
+
{ value: "en", label: "English" },
|
|
163
|
+
{ value: "fr", label: "French" },
|
|
164
|
+
{ value: "es", label: "Spanish" },
|
|
165
|
+
{ value: "sw", label: "Swahili" },
|
|
166
|
+
{ value: "pt", label: "Portuguese" },
|
|
167
|
+
{ value: "ar", label: "Arabic" },
|
|
168
|
+
{ value: "zh", label: "Chinese" },
|
|
169
|
+
{ value: "hi", label: "Hindi" },
|
|
170
|
+
{ value: "dz", label: "Dzongkha" }
|
|
171
|
+
];
|
|
172
|
+
function PersonaSettings({ adminPassword }) {
|
|
162
173
|
const config = useSiteConfig();
|
|
163
174
|
if (!config.personaEndpoint) return null;
|
|
164
|
-
return /* @__PURE__ */ jsx2(PersonaSettingsInner, {});
|
|
175
|
+
return /* @__PURE__ */ jsx2(PersonaSettingsInner, { adminPassword: adminPassword ?? null });
|
|
165
176
|
}
|
|
166
|
-
function PersonaSettingsInner() {
|
|
177
|
+
function PersonaSettingsInner({ adminPassword }) {
|
|
167
178
|
const config = useSiteConfig();
|
|
168
179
|
const persona = usePersonaContext();
|
|
169
180
|
if (!persona) return null;
|
|
170
|
-
const { persona: data, isLoaded,
|
|
181
|
+
const { persona: data, isLoaded, uploadAvatar, uploadVoice, deleteVoice, setActiveVoice, previewVoice, updateConfig } = persona;
|
|
182
|
+
const isAdmin = adminPassword !== null;
|
|
183
|
+
const handleSharedSave = useCallback2(async (fields) => {
|
|
184
|
+
if (!adminPassword) return;
|
|
185
|
+
try {
|
|
186
|
+
await updateConfig(fields, adminPassword);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.error("Settings save failed:", err);
|
|
189
|
+
}
|
|
190
|
+
}, [adminPassword, updateConfig]);
|
|
171
191
|
if (!isLoaded) {
|
|
172
192
|
return /* @__PURE__ */ jsx2("div", { style: { padding: 16, fontSize: 13, color: "#9ca3af", fontFamily: "inherit" }, children: "Loading persona settings..." });
|
|
173
193
|
}
|
|
174
|
-
return /* @__PURE__ */
|
|
175
|
-
/* @__PURE__ */ jsx2(AvatarSection, { avatarUrl: config.avatarUrl, name: config.copilotName, onUpload: uploadAvatar }),
|
|
176
|
-
/* @__PURE__ */ jsx2(NameSection, { name: config.copilotName, onSave:
|
|
194
|
+
return /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexDirection: "column", gap: 12, fontFamily: "inherit" }, children: isAdmin ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
195
|
+
/* @__PURE__ */ jsx2(AvatarSection, { avatarUrl: config.avatarUrl, name: config.copilotName, onUpload: (f) => uploadAvatar(f, adminPassword) }),
|
|
196
|
+
/* @__PURE__ */ jsx2(NameSection, { name: config.copilotName, onSave: (n) => updateConfig({ copilotName: n }, adminPassword), primaryColor: config.colors.primary }),
|
|
177
197
|
/* @__PURE__ */ jsx2(
|
|
178
198
|
VoiceSection,
|
|
179
199
|
{
|
|
180
200
|
voices: data?.voices ?? [],
|
|
181
201
|
activeVoiceId: data?.activeVoiceId ?? "",
|
|
182
|
-
onUpload: uploadVoice,
|
|
183
|
-
onDelete: deleteVoice,
|
|
184
|
-
onSelect: setActiveVoice,
|
|
202
|
+
onUpload: (f, n) => uploadVoice(f, n, adminPassword),
|
|
203
|
+
onDelete: (id) => deleteVoice(id, adminPassword),
|
|
204
|
+
onSelect: (id) => setActiveVoice(id, adminPassword),
|
|
185
205
|
onPreview: previewVoice,
|
|
186
206
|
primaryColor: config.colors.primary
|
|
187
207
|
}
|
|
208
|
+
),
|
|
209
|
+
/* @__PURE__ */ jsxs("div", { style: { borderTop: "1px solid #e5e7eb", paddingTop: 12, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
210
|
+
/* @__PURE__ */ jsx2("span", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Copilot settings" }),
|
|
211
|
+
/* @__PURE__ */ jsx2(
|
|
212
|
+
ColorSettingRow,
|
|
213
|
+
{
|
|
214
|
+
label: "Color",
|
|
215
|
+
value: data?.copilotColor || config.colors.primary || "#1B5E20",
|
|
216
|
+
onSave: (v) => handleSharedSave({ copilotColor: v })
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
/* @__PURE__ */ jsx2(
|
|
220
|
+
TextSettingRow,
|
|
221
|
+
{
|
|
222
|
+
label: "Site title",
|
|
223
|
+
value: data?.siteTitle || "",
|
|
224
|
+
onSave: (v) => handleSharedSave({ siteTitle: v })
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
/* @__PURE__ */ jsx2(
|
|
228
|
+
TextAreaSettingRow,
|
|
229
|
+
{
|
|
230
|
+
label: "Greeting",
|
|
231
|
+
value: data?.greetingMessage || "",
|
|
232
|
+
onSave: (v) => handleSharedSave({ greetingMessage: v })
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
/* @__PURE__ */ jsx2(
|
|
236
|
+
TextAreaSettingRow,
|
|
237
|
+
{
|
|
238
|
+
label: "Farewell",
|
|
239
|
+
value: data?.farewellMessage || "",
|
|
240
|
+
onSave: (v) => handleSharedSave({ farewellMessage: v })
|
|
241
|
+
}
|
|
242
|
+
),
|
|
243
|
+
/* @__PURE__ */ jsx2(
|
|
244
|
+
TextAreaSettingRow,
|
|
245
|
+
{
|
|
246
|
+
label: "System prompt intro",
|
|
247
|
+
value: data?.systemPromptIntro || "",
|
|
248
|
+
onSave: (v) => handleSharedSave({ systemPromptIntro: v }),
|
|
249
|
+
rows: 4
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
/* @__PURE__ */ jsx2(SettingRow, { label: "Default language", children: /* @__PURE__ */ jsx2(
|
|
253
|
+
"select",
|
|
254
|
+
{
|
|
255
|
+
value: data?.language || "en",
|
|
256
|
+
onChange: (e) => handleSharedSave({ language: e.target.value }),
|
|
257
|
+
style: {
|
|
258
|
+
fontSize: 12,
|
|
259
|
+
borderRadius: 6,
|
|
260
|
+
border: "1px solid #e5e7eb",
|
|
261
|
+
padding: "4px 8px",
|
|
262
|
+
outline: "none",
|
|
263
|
+
fontFamily: "inherit",
|
|
264
|
+
backgroundColor: "#fff"
|
|
265
|
+
},
|
|
266
|
+
children: LANGUAGE_OPTIONS.map((l) => /* @__PURE__ */ jsx2("option", { value: l.value, children: l.label }, l.value))
|
|
267
|
+
}
|
|
268
|
+
) })
|
|
269
|
+
] })
|
|
270
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
271
|
+
/* @__PURE__ */ jsx2(AvatarSection, { avatarUrl: config.avatarUrl, name: config.copilotName, disabled: true }),
|
|
272
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: 13, color: "#6b7280", padding: "4px 0" }, children: [
|
|
273
|
+
/* @__PURE__ */ jsx2("span", { style: { fontWeight: 500, color: "#111827" }, children: "Name:" }),
|
|
274
|
+
" ",
|
|
275
|
+
config.copilotName
|
|
276
|
+
] }),
|
|
277
|
+
/* @__PURE__ */ jsx2(
|
|
278
|
+
VoiceSection,
|
|
279
|
+
{
|
|
280
|
+
voices: data?.voices ?? [],
|
|
281
|
+
activeVoiceId: data?.activeVoiceId ?? "",
|
|
282
|
+
onUpload: (f, n) => uploadVoice(f, n),
|
|
283
|
+
onDelete: (id) => deleteVoice(id),
|
|
284
|
+
onSelect: setActiveVoice,
|
|
285
|
+
onPreview: previewVoice,
|
|
286
|
+
primaryColor: config.colors.primary,
|
|
287
|
+
disabled: true
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
] }) });
|
|
291
|
+
}
|
|
292
|
+
function SettingRow({ label, children }) {
|
|
293
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
294
|
+
/* @__PURE__ */ jsx2("span", { style: { fontSize: 12, color: "#6b7280", minWidth: 90 }, children: label }),
|
|
295
|
+
/* @__PURE__ */ jsx2("div", { style: { flex: 1 }, children })
|
|
296
|
+
] });
|
|
297
|
+
}
|
|
298
|
+
function ColorSettingRow({ label, value, onSave }) {
|
|
299
|
+
const [local, setLocal] = useState2(value);
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
setLocal(value);
|
|
302
|
+
}, [value]);
|
|
303
|
+
return /* @__PURE__ */ jsx2(SettingRow, { label, children: /* @__PURE__ */ jsx2(
|
|
304
|
+
"input",
|
|
305
|
+
{
|
|
306
|
+
type: "color",
|
|
307
|
+
value: local,
|
|
308
|
+
onChange: (e) => setLocal(e.target.value),
|
|
309
|
+
onBlur: () => {
|
|
310
|
+
if (local !== value) onSave(local);
|
|
311
|
+
},
|
|
312
|
+
style: { width: 32, height: 26, border: "1px solid #e5e7eb", borderRadius: 4, cursor: "pointer", padding: 0 }
|
|
313
|
+
}
|
|
314
|
+
) });
|
|
315
|
+
}
|
|
316
|
+
function TextSettingRow({ label, value, onSave }) {
|
|
317
|
+
const [local, setLocal] = useState2(value);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
setLocal(value);
|
|
320
|
+
}, [value]);
|
|
321
|
+
return /* @__PURE__ */ jsx2(SettingRow, { label, children: /* @__PURE__ */ jsx2(
|
|
322
|
+
"input",
|
|
323
|
+
{
|
|
324
|
+
type: "text",
|
|
325
|
+
value: local,
|
|
326
|
+
onChange: (e) => setLocal(e.target.value),
|
|
327
|
+
onBlur: () => {
|
|
328
|
+
if (local !== value) onSave(local);
|
|
329
|
+
},
|
|
330
|
+
style: {
|
|
331
|
+
width: "100%",
|
|
332
|
+
fontSize: 12,
|
|
333
|
+
padding: "4px 8px",
|
|
334
|
+
borderRadius: 6,
|
|
335
|
+
border: "1px solid #e5e7eb",
|
|
336
|
+
outline: "none",
|
|
337
|
+
fontFamily: "inherit",
|
|
338
|
+
boxSizing: "border-box"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
) });
|
|
342
|
+
}
|
|
343
|
+
function TextAreaSettingRow({ label, value, onSave, rows = 2 }) {
|
|
344
|
+
const [local, setLocal] = useState2(value);
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
setLocal(value);
|
|
347
|
+
}, [value]);
|
|
348
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
|
|
349
|
+
/* @__PURE__ */ jsx2("span", { style: { fontSize: 12, color: "#6b7280" }, children: label }),
|
|
350
|
+
/* @__PURE__ */ jsx2(
|
|
351
|
+
"textarea",
|
|
352
|
+
{
|
|
353
|
+
value: local,
|
|
354
|
+
onChange: (e) => setLocal(e.target.value),
|
|
355
|
+
onBlur: () => {
|
|
356
|
+
if (local !== value) onSave(local);
|
|
357
|
+
},
|
|
358
|
+
rows,
|
|
359
|
+
style: {
|
|
360
|
+
width: "100%",
|
|
361
|
+
fontSize: 12,
|
|
362
|
+
padding: "6px 8px",
|
|
363
|
+
borderRadius: 6,
|
|
364
|
+
border: "1px solid #e5e7eb",
|
|
365
|
+
outline: "none",
|
|
366
|
+
fontFamily: "inherit",
|
|
367
|
+
resize: "vertical",
|
|
368
|
+
boxSizing: "border-box"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
188
371
|
)
|
|
189
372
|
] });
|
|
190
373
|
}
|
|
191
|
-
function AvatarSection({ avatarUrl, name, onUpload }) {
|
|
374
|
+
function AvatarSection({ avatarUrl, name, onUpload, disabled }) {
|
|
192
375
|
const [uploading, setUploading] = useState2(false);
|
|
193
376
|
const [hovered, setHovered] = useState2(false);
|
|
194
377
|
const [imgError, setImgError] = useState2(false);
|
|
378
|
+
const [uploadError, setUploadError] = useState2(null);
|
|
195
379
|
const inputRef = useRef2(null);
|
|
196
380
|
const initial = (name || "?")[0].toUpperCase();
|
|
197
381
|
const handleFile = async (e) => {
|
|
198
382
|
const file = e.target.files?.[0];
|
|
199
|
-
if (!file) return;
|
|
383
|
+
if (!file || !onUpload) return;
|
|
384
|
+
if (file.size > 5 * 1024 * 1024) {
|
|
385
|
+
setUploadError("Image too large (max 5 MB)");
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
200
388
|
setUploading(true);
|
|
389
|
+
setUploadError(null);
|
|
201
390
|
try {
|
|
202
391
|
await onUpload(file);
|
|
203
392
|
setImgError(false);
|
|
204
393
|
} catch (err) {
|
|
205
|
-
|
|
394
|
+
setUploadError(err instanceof Error ? err.message : "Upload failed");
|
|
206
395
|
} finally {
|
|
207
396
|
setUploading(false);
|
|
208
397
|
}
|
|
209
398
|
};
|
|
210
399
|
const showImage = avatarUrl && !imgError;
|
|
400
|
+
const canEdit = !disabled && onUpload;
|
|
211
401
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, paddingTop: 4, paddingBottom: 4 }, children: [
|
|
212
402
|
/* @__PURE__ */ jsxs(
|
|
213
403
|
"div",
|
|
214
404
|
{
|
|
215
|
-
onClick: () => !uploading && inputRef.current?.click(),
|
|
216
|
-
onMouseEnter: () => setHovered(true),
|
|
405
|
+
onClick: () => canEdit && !uploading && inputRef.current?.click(),
|
|
406
|
+
onMouseEnter: () => canEdit && setHovered(true),
|
|
217
407
|
onMouseLeave: () => setHovered(false),
|
|
218
408
|
style: {
|
|
219
409
|
position: "relative",
|
|
@@ -223,7 +413,7 @@ function AvatarSection({ avatarUrl, name, onUpload }) {
|
|
|
223
413
|
overflow: "hidden",
|
|
224
414
|
backgroundColor: "#e5e7eb",
|
|
225
415
|
flexShrink: 0,
|
|
226
|
-
cursor: uploading ? "wait" : "pointer"
|
|
416
|
+
cursor: !canEdit ? "default" : uploading ? "wait" : "pointer"
|
|
227
417
|
},
|
|
228
418
|
children: [
|
|
229
419
|
showImage ? /* @__PURE__ */ jsx2(
|
|
@@ -244,7 +434,7 @@ function AvatarSection({ avatarUrl, name, onUpload }) {
|
|
|
244
434
|
fontSize: 16,
|
|
245
435
|
fontWeight: 600
|
|
246
436
|
}, children: initial }),
|
|
247
|
-
hovered && !uploading && /* @__PURE__ */ jsx2("div", { style: {
|
|
437
|
+
canEdit && hovered && !uploading && /* @__PURE__ */ jsx2("div", { style: {
|
|
248
438
|
position: "absolute",
|
|
249
439
|
inset: 0,
|
|
250
440
|
backgroundColor: "rgba(0,0,0,0.35)",
|
|
@@ -261,9 +451,9 @@ function AvatarSection({ avatarUrl, name, onUpload }) {
|
|
|
261
451
|
),
|
|
262
452
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
263
453
|
/* @__PURE__ */ jsx2("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Avatar" }),
|
|
264
|
-
/* @__PURE__ */ jsx2("div", { style: { fontSize: 11, color: "#6b7280" }, children: uploading ? "Uploading..." : "
|
|
454
|
+
/* @__PURE__ */ jsx2("div", { style: { fontSize: 11, color: uploadError ? "#DC2626" : "#6b7280" }, children: uploadError ?? (disabled ? "" : uploading ? "Uploading..." : "PNG, JPG or WebP (max 5 MB)") })
|
|
265
455
|
] }),
|
|
266
|
-
/* @__PURE__ */ jsx2("input", { ref: inputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleFile, style: { display: "none" } })
|
|
456
|
+
canEdit && /* @__PURE__ */ jsx2("input", { ref: inputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleFile, style: { display: "none" } })
|
|
267
457
|
] });
|
|
268
458
|
}
|
|
269
459
|
function NameSection({ name, onSave, primaryColor }) {
|
|
@@ -342,7 +532,7 @@ function NameSection({ name, onSave, primaryColor }) {
|
|
|
342
532
|
] })
|
|
343
533
|
] });
|
|
344
534
|
}
|
|
345
|
-
function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onPreview, primaryColor }) {
|
|
535
|
+
function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onPreview, primaryColor, disabled }) {
|
|
346
536
|
const [uploading, setUploading] = useState2(false);
|
|
347
537
|
const [uploadName, setUploadName] = useState2("");
|
|
348
538
|
const [showUpload, setShowUpload] = useState2(false);
|
|
@@ -398,12 +588,13 @@ function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onP
|
|
|
398
588
|
isPreviewing: previewing === v.id,
|
|
399
589
|
onSelect: () => onSelect(v.id),
|
|
400
590
|
onPreview: () => handlePreview(v.id),
|
|
401
|
-
onDelete: () => onDelete(v.id),
|
|
591
|
+
onDelete: disabled ? void 0 : () => onDelete(v.id),
|
|
592
|
+
disabled,
|
|
402
593
|
primaryColor
|
|
403
594
|
},
|
|
404
595
|
v.id
|
|
405
596
|
)),
|
|
406
|
-
showUpload ? /* @__PURE__ */ jsx2(
|
|
597
|
+
!disabled && (showUpload ? /* @__PURE__ */ jsx2(
|
|
407
598
|
UploadForm,
|
|
408
599
|
{
|
|
409
600
|
uploadName,
|
|
@@ -422,11 +613,11 @@ function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onP
|
|
|
422
613
|
disabled: voices.length >= 10,
|
|
423
614
|
onClick: () => inputRef.current?.click()
|
|
424
615
|
}
|
|
425
|
-
),
|
|
426
|
-
/* @__PURE__ */ jsx2("input", { ref: inputRef, type: "file", accept: "audio/wav", onChange: handleFileSelect, style: { display: "none" } })
|
|
616
|
+
)),
|
|
617
|
+
!disabled && /* @__PURE__ */ jsx2("input", { ref: inputRef, type: "file", accept: "audio/wav", onChange: handleFileSelect, style: { display: "none" } })
|
|
427
618
|
] });
|
|
428
619
|
}
|
|
429
|
-
function VoiceRow({ voice, isActive, isPreviewing, onSelect, onPreview, onDelete, primaryColor }) {
|
|
620
|
+
function VoiceRow({ voice, isActive, isPreviewing, onSelect, onPreview, onDelete, primaryColor, disabled }) {
|
|
430
621
|
const [previewHovered, setPreviewHovered] = useState2(false);
|
|
431
622
|
const [deleteHovered, setDeleteHovered] = useState2(false);
|
|
432
623
|
return /* @__PURE__ */ jsxs("div", { style: {
|
|
@@ -449,6 +640,7 @@ function VoiceRow({ voice, isActive, isPreviewing, onSelect, onPreview, onDelete
|
|
|
449
640
|
name: "active-voice",
|
|
450
641
|
checked: isActive,
|
|
451
642
|
onChange: onSelect,
|
|
643
|
+
disabled,
|
|
452
644
|
style: { accentColor: primaryColor }
|
|
453
645
|
}
|
|
454
646
|
),
|
|
@@ -473,7 +665,7 @@ function VoiceRow({ voice, isActive, isPreviewing, onSelect, onPreview, onDelete
|
|
|
473
665
|
children: isPreviewing ? "Playing..." : "Preview"
|
|
474
666
|
}
|
|
475
667
|
),
|
|
476
|
-
/* @__PURE__ */ jsx2(
|
|
668
|
+
onDelete && /* @__PURE__ */ jsx2(
|
|
477
669
|
"button",
|
|
478
670
|
{
|
|
479
671
|
onClick: onDelete,
|
|
@@ -605,7 +797,8 @@ function UploadButton({ disabled, onClick }) {
|
|
|
605
797
|
}
|
|
606
798
|
|
|
607
799
|
// src/components/VoiceSettingsView.tsx
|
|
608
|
-
import {
|
|
800
|
+
import { Lock, Unlock } from "lucide-react";
|
|
801
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
609
802
|
function expressivenessLabel(v) {
|
|
610
803
|
if (v <= 0.15) return "Low";
|
|
611
804
|
if (v <= 0.4) return "Medium";
|
|
@@ -621,7 +814,7 @@ function bargeInLabel(v) {
|
|
|
621
814
|
if (v >= 0.6) return "Normal";
|
|
622
815
|
return "Easy";
|
|
623
816
|
}
|
|
624
|
-
var
|
|
817
|
+
var LANGUAGE_OPTIONS2 = [
|
|
625
818
|
{ value: "en", label: "English" },
|
|
626
819
|
{ value: "fr", label: "French" },
|
|
627
820
|
{ value: "es", label: "Spanish" },
|
|
@@ -784,7 +977,7 @@ function SelectSetting({
|
|
|
784
977
|
}) {
|
|
785
978
|
return /* @__PURE__ */ jsxs2("div", { style: { paddingTop: 12, paddingBottom: 12, display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
786
979
|
icon,
|
|
787
|
-
/* @__PURE__ */ jsx3("span", { style: { flex: 1, fontSize: 13, fontWeight: 500, color: "#111827" }, children: label }),
|
|
980
|
+
/* @__PURE__ */ jsx3("span", { style: { flex: 1, fontSize: 13, fontWeight: 500, color: "#111827", minWidth: 0 }, children: label }),
|
|
788
981
|
/* @__PURE__ */ jsx3(
|
|
789
982
|
"select",
|
|
790
983
|
{
|
|
@@ -805,19 +998,24 @@ function SelectSetting({
|
|
|
805
998
|
e.currentTarget.style.borderColor = "#e5e7eb";
|
|
806
999
|
},
|
|
807
1000
|
style: {
|
|
808
|
-
height:
|
|
1001
|
+
height: 34,
|
|
1002
|
+
lineHeight: "34px",
|
|
809
1003
|
fontSize: 12,
|
|
810
1004
|
fontWeight: 500,
|
|
811
1005
|
color: "#374151",
|
|
812
1006
|
borderRadius: 9999,
|
|
813
1007
|
border: "1px solid #e5e7eb",
|
|
814
1008
|
backgroundColor: "#f9fafb",
|
|
1009
|
+
paddingTop: 0,
|
|
1010
|
+
paddingBottom: 0,
|
|
815
1011
|
paddingLeft: 12,
|
|
816
1012
|
paddingRight: 28,
|
|
817
1013
|
outline: "none",
|
|
818
1014
|
WebkitAppearance: "none",
|
|
819
1015
|
appearance: "none",
|
|
820
1016
|
cursor: "pointer",
|
|
1017
|
+
flexShrink: 0,
|
|
1018
|
+
width: 110,
|
|
821
1019
|
transition: "border-color 0.15s, background-color 0.15s",
|
|
822
1020
|
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`,
|
|
823
1021
|
backgroundRepeat: "no-repeat",
|
|
@@ -831,6 +1029,7 @@ function SelectSetting({
|
|
|
831
1029
|
function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
832
1030
|
const { settings, updateSetting, resetSettings } = useVoiceSettings();
|
|
833
1031
|
const config = useSiteConfig2();
|
|
1032
|
+
const persona = usePersonaContext2();
|
|
834
1033
|
const { colors } = config;
|
|
835
1034
|
const [openSection, setOpenSection] = useState3(null);
|
|
836
1035
|
const iconStyle = { width: 16, height: 16, flexShrink: 0, color: colors.primary };
|
|
@@ -839,6 +1038,36 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
|
839
1038
|
open: openSection === id,
|
|
840
1039
|
onToggle: () => setOpenSection(openSection === id ? null : id)
|
|
841
1040
|
});
|
|
1041
|
+
const [adminPassword, setAdminPassword] = useState3(
|
|
1042
|
+
() => sessionStorage.getItem("voice-admin-pw")
|
|
1043
|
+
);
|
|
1044
|
+
const [showPasswordInput, setShowPasswordInput] = useState3(false);
|
|
1045
|
+
const [passwordInput, setPasswordInput] = useState3("");
|
|
1046
|
+
const [authError, setAuthError] = useState3("");
|
|
1047
|
+
const isAdmin = adminPassword !== null;
|
|
1048
|
+
const handleAdminLogin = useCallback3(async (pw) => {
|
|
1049
|
+
if (!persona) return;
|
|
1050
|
+
try {
|
|
1051
|
+
await persona.updateConfig({}, pw);
|
|
1052
|
+
sessionStorage.setItem("voice-admin-pw", pw);
|
|
1053
|
+
setAdminPassword(pw);
|
|
1054
|
+
setAuthError("");
|
|
1055
|
+
setShowPasswordInput(false);
|
|
1056
|
+
setPasswordInput("");
|
|
1057
|
+
} catch {
|
|
1058
|
+
setAuthError("Invalid password");
|
|
1059
|
+
}
|
|
1060
|
+
}, [persona]);
|
|
1061
|
+
const handleAdminToggle = useCallback3(() => {
|
|
1062
|
+
if (isAdmin) {
|
|
1063
|
+
sessionStorage.removeItem("voice-admin-pw");
|
|
1064
|
+
setAdminPassword(null);
|
|
1065
|
+
setShowPasswordInput(false);
|
|
1066
|
+
setPasswordInput("");
|
|
1067
|
+
} else {
|
|
1068
|
+
setShowPasswordInput(true);
|
|
1069
|
+
}
|
|
1070
|
+
}, [isAdmin]);
|
|
842
1071
|
return /* @__PURE__ */ jsxs2(
|
|
843
1072
|
motion.div,
|
|
844
1073
|
{
|
|
@@ -899,7 +1128,7 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
|
899
1128
|
}
|
|
900
1129
|
),
|
|
901
1130
|
/* @__PURE__ */ jsxs2("div", { style: { flex: 1, minHeight: 0, overflowY: "auto" }, children: [
|
|
902
|
-
config.personaEndpoint && /* @__PURE__ */ jsx3(SettingsSection, { title: "Persona", icon: /* @__PURE__ */ jsx3(User, { style: sectionIconStyle }), ...sectionProps("persona"), children: /* @__PURE__ */ jsx3(PersonaSettings, {}) }),
|
|
1131
|
+
config.personaEndpoint && /* @__PURE__ */ jsx3(SettingsSection, { title: "Persona", icon: /* @__PURE__ */ jsx3(User, { style: sectionIconStyle }), ...sectionProps("persona"), children: /* @__PURE__ */ jsx3(PersonaSettings, { adminPassword }) }),
|
|
903
1132
|
/* @__PURE__ */ jsxs2(SettingsSection, { title: "Conversation", icon: /* @__PURE__ */ jsx3(MessageCircle, { style: sectionIconStyle }), ...sectionProps("conversation"), children: [
|
|
904
1133
|
/* @__PURE__ */ jsx3(
|
|
905
1134
|
SelectSetting,
|
|
@@ -940,7 +1169,7 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
|
940
1169
|
label: "Language",
|
|
941
1170
|
value: settings.language,
|
|
942
1171
|
onChange: (v) => updateSetting("language", v),
|
|
943
|
-
options:
|
|
1172
|
+
options: LANGUAGE_OPTIONS2
|
|
944
1173
|
}
|
|
945
1174
|
),
|
|
946
1175
|
/* @__PURE__ */ jsx3(Divider, {}),
|
|
@@ -1185,10 +1414,100 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
|
1185
1414
|
] })
|
|
1186
1415
|
] })
|
|
1187
1416
|
] }),
|
|
1188
|
-
/* @__PURE__ */
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1417
|
+
/* @__PURE__ */ jsx3("div", { style: { flexShrink: 0, padding: "8px 16px", fontSize: 11, color: "#9ca3af", borderTop: "1px solid #f3f4f6" }, children: showPasswordInput ? /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 6, alignItems: "center", justifyContent: "center", padding: "4px 0" }, children: [
|
|
1418
|
+
/* @__PURE__ */ jsx3(
|
|
1419
|
+
"input",
|
|
1420
|
+
{
|
|
1421
|
+
type: "password",
|
|
1422
|
+
placeholder: "Admin password",
|
|
1423
|
+
value: passwordInput,
|
|
1424
|
+
onChange: (e) => {
|
|
1425
|
+
setPasswordInput(e.target.value);
|
|
1426
|
+
setAuthError("");
|
|
1427
|
+
},
|
|
1428
|
+
onKeyDown: (e) => {
|
|
1429
|
+
if (e.key === "Enter") handleAdminLogin(passwordInput);
|
|
1430
|
+
if (e.key === "Escape") {
|
|
1431
|
+
setShowPasswordInput(false);
|
|
1432
|
+
setPasswordInput("");
|
|
1433
|
+
setAuthError("");
|
|
1434
|
+
}
|
|
1435
|
+
},
|
|
1436
|
+
autoFocus: true,
|
|
1437
|
+
style: {
|
|
1438
|
+
fontSize: 11,
|
|
1439
|
+
padding: "3px 8px",
|
|
1440
|
+
borderRadius: 6,
|
|
1441
|
+
border: `1px solid ${authError ? "#ef4444" : "#e5e7eb"}`,
|
|
1442
|
+
outline: "none",
|
|
1443
|
+
fontFamily: "inherit",
|
|
1444
|
+
width: 110
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
),
|
|
1448
|
+
/* @__PURE__ */ jsx3(
|
|
1449
|
+
"button",
|
|
1450
|
+
{
|
|
1451
|
+
onClick: () => handleAdminLogin(passwordInput),
|
|
1452
|
+
style: {
|
|
1453
|
+
fontSize: 10,
|
|
1454
|
+
fontWeight: 500,
|
|
1455
|
+
padding: "3px 8px",
|
|
1456
|
+
borderRadius: 6,
|
|
1457
|
+
border: "none",
|
|
1458
|
+
backgroundColor: "#1f2937",
|
|
1459
|
+
color: "#fff",
|
|
1460
|
+
cursor: "pointer",
|
|
1461
|
+
fontFamily: "inherit"
|
|
1462
|
+
},
|
|
1463
|
+
children: "OK"
|
|
1464
|
+
}
|
|
1465
|
+
),
|
|
1466
|
+
/* @__PURE__ */ jsx3(
|
|
1467
|
+
"button",
|
|
1468
|
+
{
|
|
1469
|
+
onClick: () => {
|
|
1470
|
+
setShowPasswordInput(false);
|
|
1471
|
+
setPasswordInput("");
|
|
1472
|
+
setAuthError("");
|
|
1473
|
+
},
|
|
1474
|
+
style: { fontSize: 10, color: "#9ca3af", background: "none", border: "none", cursor: "pointer", fontFamily: "inherit" },
|
|
1475
|
+
children: "Cancel"
|
|
1476
|
+
}
|
|
1477
|
+
),
|
|
1478
|
+
authError && /* @__PURE__ */ jsx3("span", { style: { fontSize: 10, color: "#ef4444" }, children: authError })
|
|
1479
|
+
] }) : /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1480
|
+
config.personaEndpoint ? /* @__PURE__ */ jsx3(
|
|
1481
|
+
"button",
|
|
1482
|
+
{
|
|
1483
|
+
onClick: handleAdminToggle,
|
|
1484
|
+
style: {
|
|
1485
|
+
display: "flex",
|
|
1486
|
+
alignItems: "center",
|
|
1487
|
+
gap: 4,
|
|
1488
|
+
fontSize: 11,
|
|
1489
|
+
color: isAdmin ? colors.primary : "#9ca3af",
|
|
1490
|
+
background: "none",
|
|
1491
|
+
border: "none",
|
|
1492
|
+
cursor: "pointer",
|
|
1493
|
+
fontFamily: "inherit",
|
|
1494
|
+
padding: 0,
|
|
1495
|
+
transition: "color 0.15s"
|
|
1496
|
+
},
|
|
1497
|
+
children: isAdmin ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1498
|
+
/* @__PURE__ */ jsx3(Unlock, { style: { width: 12, height: 12 } }),
|
|
1499
|
+
" Admin mode"
|
|
1500
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1501
|
+
/* @__PURE__ */ jsx3(Lock, { style: { width: 12, height: 12 } }),
|
|
1502
|
+
" Admin mode"
|
|
1503
|
+
] })
|
|
1504
|
+
}
|
|
1505
|
+
) : /* @__PURE__ */ jsx3("span", {}),
|
|
1506
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1507
|
+
"Kit v",
|
|
1508
|
+
/* @__PURE__ */ jsx3("span", { style: { fontWeight: 500, color: "#6b7280" }, children: "5.0.1" })
|
|
1509
|
+
] })
|
|
1510
|
+
] }) })
|
|
1192
1511
|
]
|
|
1193
1512
|
}
|
|
1194
1513
|
);
|
|
@@ -1278,4 +1597,4 @@ export {
|
|
|
1278
1597
|
SettingsSection,
|
|
1279
1598
|
Divider
|
|
1280
1599
|
};
|
|
1281
|
-
//# sourceMappingURL=chunk-
|
|
1600
|
+
//# sourceMappingURL=chunk-K3JJWWRQ.js.map
|