@unctad-ai/voice-agent-ui 1.0.7 → 1.0.8
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-NEKSYOW7.js → VoiceSettingsView-PXEMZLVK.js} +2 -2
- package/dist/{chunk-UFETQCDM.js → chunk-YCJGLETL.js} +47 -8
- package/dist/chunk-YCJGLETL.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-UFETQCDM.js.map +0 -1
- /package/dist/{VoiceSettingsView-NEKSYOW7.js.map → VoiceSettingsView-PXEMZLVK.js.map} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
SliderSetting,
|
|
6
6
|
ToggleSetting,
|
|
7
7
|
VoiceSettingsView
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YCJGLETL.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-PXEMZLVK.js.map
|
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
MessageCircle,
|
|
26
26
|
Headphones,
|
|
27
27
|
SlidersHorizontal,
|
|
28
|
-
Wrench
|
|
28
|
+
Wrench,
|
|
29
|
+
Globe
|
|
29
30
|
} from "lucide-react";
|
|
30
31
|
|
|
31
32
|
// src/contexts/VoiceSettingsContext.tsx
|
|
@@ -48,7 +49,8 @@ import {
|
|
|
48
49
|
DEFAULT_TTS_TIMEOUT_MS,
|
|
49
50
|
DEFAULT_LLM_TIMEOUT_MS,
|
|
50
51
|
DEFAULT_MIN_AUDIO_RMS,
|
|
51
|
-
DEFAULT_MAX_HISTORY_MESSAGES
|
|
52
|
+
DEFAULT_MAX_HISTORY_MESSAGES,
|
|
53
|
+
DEFAULT_LANGUAGE
|
|
52
54
|
} from "@unctad-ai/voice-agent-core";
|
|
53
55
|
import { jsx } from "react/jsx-runtime";
|
|
54
56
|
var DEFAULTS = {
|
|
@@ -69,7 +71,8 @@ var DEFAULTS = {
|
|
|
69
71
|
ttsTimeoutMs: DEFAULT_TTS_TIMEOUT_MS,
|
|
70
72
|
llmTimeoutMs: DEFAULT_LLM_TIMEOUT_MS,
|
|
71
73
|
minAudioRms: DEFAULT_MIN_AUDIO_RMS,
|
|
72
|
-
maxHistoryMessages: DEFAULT_MAX_HISTORY_MESSAGES
|
|
74
|
+
maxHistoryMessages: DEFAULT_MAX_HISTORY_MESSAGES,
|
|
75
|
+
language: DEFAULT_LANGUAGE
|
|
73
76
|
};
|
|
74
77
|
var STORAGE_KEY = "voice-settings";
|
|
75
78
|
function loadSettings() {
|
|
@@ -89,10 +92,24 @@ function persistSettings(settings) {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
var VoiceSettingsContext = createContext(void 0);
|
|
92
|
-
function VoiceSettingsProvider({ children }) {
|
|
93
|
-
const [settings, setSettings] = useState(
|
|
95
|
+
function VoiceSettingsProvider({ children, siteLanguage }) {
|
|
96
|
+
const [settings, setSettings] = useState(() => {
|
|
97
|
+
const loaded = loadSettings();
|
|
98
|
+
if (siteLanguage) {
|
|
99
|
+
try {
|
|
100
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
101
|
+
const hasPersisted = raw ? Object.prototype.hasOwnProperty.call(JSON.parse(raw), "language") : false;
|
|
102
|
+
if (!hasPersisted) loaded.language = siteLanguage;
|
|
103
|
+
} catch {
|
|
104
|
+
loaded.language = siteLanguage;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return loaded;
|
|
108
|
+
});
|
|
94
109
|
const volumeRef = useRef(settings.volume);
|
|
95
110
|
const speedRef = useRef(settings.playbackSpeed);
|
|
111
|
+
const siteLanguageRef = useRef(siteLanguage);
|
|
112
|
+
siteLanguageRef.current = siteLanguage;
|
|
96
113
|
const updateSetting = useCallback(
|
|
97
114
|
(key, value) => {
|
|
98
115
|
setSettings((prev) => {
|
|
@@ -106,7 +123,7 @@ function VoiceSettingsProvider({ children }) {
|
|
|
106
123
|
[]
|
|
107
124
|
);
|
|
108
125
|
const resetSettings = useCallback(() => {
|
|
109
|
-
const defaults = { ...DEFAULTS };
|
|
126
|
+
const defaults = { ...DEFAULTS, language: siteLanguageRef.current ?? DEFAULT_LANGUAGE };
|
|
110
127
|
setSettings(defaults);
|
|
111
128
|
persistSettings(defaults);
|
|
112
129
|
volumeRef.current = defaults.volume;
|
|
@@ -604,6 +621,17 @@ function bargeInLabel(v) {
|
|
|
604
621
|
if (v >= 0.6) return "Normal";
|
|
605
622
|
return "Easy";
|
|
606
623
|
}
|
|
624
|
+
var LANGUAGE_OPTIONS = [
|
|
625
|
+
{ value: "en", label: "English" },
|
|
626
|
+
{ value: "fr", label: "French" },
|
|
627
|
+
{ value: "es", label: "Spanish" },
|
|
628
|
+
{ value: "sw", label: "Swahili" },
|
|
629
|
+
{ value: "pt", label: "Portuguese" },
|
|
630
|
+
{ value: "ar", label: "Arabic" },
|
|
631
|
+
{ value: "zh", label: "Chinese" },
|
|
632
|
+
{ value: "hi", label: "Hindi" },
|
|
633
|
+
{ value: "dz", label: "Dzongkha" }
|
|
634
|
+
];
|
|
607
635
|
var sliderStylesInjected = false;
|
|
608
636
|
function ensureSliderStyles() {
|
|
609
637
|
if (sliderStylesInjected || typeof document === "undefined") return;
|
|
@@ -905,6 +933,17 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
|
905
933
|
)
|
|
906
934
|
] }),
|
|
907
935
|
/* @__PURE__ */ jsxs2(SettingsSection, { title: "Listening", icon: /* @__PURE__ */ jsx3(Headphones, { style: sectionIconStyle }), ...sectionProps("listening"), children: [
|
|
936
|
+
/* @__PURE__ */ jsx3(
|
|
937
|
+
SelectSetting,
|
|
938
|
+
{
|
|
939
|
+
icon: /* @__PURE__ */ jsx3(Globe, { style: iconStyle }),
|
|
940
|
+
label: "Language",
|
|
941
|
+
value: settings.language,
|
|
942
|
+
onChange: (v) => updateSetting("language", v),
|
|
943
|
+
options: LANGUAGE_OPTIONS
|
|
944
|
+
}
|
|
945
|
+
),
|
|
946
|
+
/* @__PURE__ */ jsx3(Divider, {}),
|
|
908
947
|
/* @__PURE__ */ jsx3(
|
|
909
948
|
ToggleSetting,
|
|
910
949
|
{
|
|
@@ -1146,7 +1185,7 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
|
1146
1185
|
] }),
|
|
1147
1186
|
/* @__PURE__ */ jsxs2("div", { style: { paddingTop: 2, paddingBottom: 4, fontSize: 11, color: "#9ca3af" }, children: [
|
|
1148
1187
|
"Kit version: ",
|
|
1149
|
-
/* @__PURE__ */ jsx3("span", { style: { fontWeight: 500, color: "#6b7280" }, children: "1.0.
|
|
1188
|
+
/* @__PURE__ */ jsx3("span", { style: { fontWeight: 500, color: "#6b7280" }, children: "1.0.8" })
|
|
1150
1189
|
] })
|
|
1151
1190
|
] })
|
|
1152
1191
|
] })
|
|
@@ -1239,4 +1278,4 @@ export {
|
|
|
1239
1278
|
SettingsSection,
|
|
1240
1279
|
Divider
|
|
1241
1280
|
};
|
|
1242
|
-
//# sourceMappingURL=chunk-
|
|
1281
|
+
//# sourceMappingURL=chunk-YCJGLETL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/VoiceSettingsView.tsx","../src/contexts/VoiceSettingsContext.tsx","../src/components/PersonaSettings.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport {\n ArrowLeft,\n RotateCcw,\n Volume2,\n Gauge,\n AudioLines,\n Sparkles,\n Mic,\n Timer,\n MessageSquare,\n Activity,\n Cpu,\n EyeOff,\n Info,\n Ear,\n Clock,\n Zap,\n Minimize2,\n Signal,\n History,\n ChevronDown,\n User,\n MessageCircle,\n Headphones,\n SlidersHorizontal,\n Wrench,\n Globe,\n} from 'lucide-react';\nimport { useVoiceSettings } from '../contexts/VoiceSettingsContext';\nimport { VAD, useSiteConfig } from '@unctad-ai/voice-agent-core';\nimport { PersonaSettings } from './PersonaSettings.js';\n\ninterface VoiceSettingsViewProps {\n onBack: () => void;\n onVolumeChange?: (v: number) => void;\n}\n\nfunction expressivenessLabel(v: number): string {\n if (v <= 0.15) return 'Low';\n if (v <= 0.4) return 'Medium';\n return 'High';\n}\n\nfunction speechThresholdLabel(v: number): string {\n if (v >= 0.75) return 'Strict';\n if (v >= 0.5) return 'Balanced';\n return 'Sensitive';\n}\n\nfunction bargeInLabel(v: number): string {\n if (v >= 0.8) return 'Hard';\n if (v >= 0.6) return 'Normal';\n return 'Easy';\n}\n\nconst LANGUAGE_OPTIONS = [\n { value: 'en', label: 'English' },\n { value: 'fr', label: 'French' },\n { value: 'es', label: 'Spanish' },\n { value: 'sw', label: 'Swahili' },\n { value: 'pt', label: 'Portuguese' },\n { value: 'ar', label: 'Arabic' },\n { value: 'zh', label: 'Chinese' },\n { value: 'hi', label: 'Hindi' },\n { value: 'dz', label: 'Dzongkha' },\n];\n\n/** Inline style tag for custom range slider — injected once */\nlet sliderStylesInjected = false;\nfunction ensureSliderStyles() {\n if (sliderStylesInjected || typeof document === 'undefined') return;\n sliderStylesInjected = true;\n const style = document.createElement('style');\n style.textContent = `\n input[type=\"range\"].voice-slider {\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n height: 4px;\n border-radius: 2px;\n background: #e5e7eb;\n outline: none;\n cursor: pointer;\n margin: 6px 0;\n }\n input[type=\"range\"].voice-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--voice-settings-accent, #DB2129);\n border: 2px solid #fff;\n box-shadow: 0 1px 3px rgba(0,0,0,0.15);\n cursor: pointer;\n transition: transform 0.1s ease;\n margin-top: -6px;\n }\n input[type=\"range\"].voice-slider::-webkit-slider-thumb:hover {\n transform: scale(1.15);\n }\n input[type=\"range\"].voice-slider::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--voice-settings-accent, #DB2129);\n border: 2px solid #fff;\n box-shadow: 0 1px 3px rgba(0,0,0,0.15);\n cursor: pointer;\n }\n input[type=\"range\"].voice-slider::-webkit-slider-runnable-track {\n height: 4px;\n border-radius: 2px;\n display: flex;\n align-items: center;\n }\n input[type=\"range\"].voice-slider::-moz-range-track {\n height: 4px;\n border-radius: 2px;\n background: #e5e7eb;\n }\n `;\n document.head.appendChild(style);\n}\n\n/** Slider setting row */\nexport function SliderSetting({\n icon,\n label,\n value,\n displayValue,\n min,\n max,\n step,\n onChange,\n}: {\n icon: React.ReactNode;\n label: string;\n value: number;\n displayValue: string;\n min: number;\n max: number;\n step: number;\n onChange: (v: number) => void;\n}) {\n ensureSliderStyles();\n const pct = ((value - min) / (max - min)) * 100;\n\n return (\n <div style={{ paddingTop: 12, paddingBottom: 12, display: 'flex', flexDirection: 'column', gap: 10 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>\n {icon}\n <span style={{ flex: 1, fontSize: 13, fontWeight: 500, color: '#111827' }}>{label}</span>\n <span style={{ fontSize: 11, fontWeight: 500, fontVariantNumeric: 'tabular-nums', color: 'var(--voice-settings-accent, #DB2129)' }}>{displayValue}</span>\n </div>\n <input\n type=\"range\"\n className=\"voice-slider\"\n value={value}\n min={min}\n max={max}\n step={step}\n onChange={(e) => onChange(Number(e.target.value))}\n style={{\n background: `linear-gradient(to right, var(--voice-settings-accent, #DB2129) 0%, var(--voice-settings-accent, #DB2129) ${pct}%, #e5e7eb ${pct}%, #e5e7eb 100%)`,\n }}\n />\n </div>\n );\n}\n\n/** Toggle setting row */\nexport function ToggleSetting({\n icon,\n label,\n description,\n checked,\n onChange,\n}: {\n icon: React.ReactNode;\n label: string;\n description: string;\n checked: boolean;\n onChange: (v: boolean) => void;\n}) {\n return (\n <label style={{ paddingTop: 12, paddingBottom: 12, display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer' }}>\n {icon}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 13, fontWeight: 500, color: '#111827' }}>{label}</div>\n <div style={{ fontSize: 11, color: '#6b7280' }}>{description}</div>\n </div>\n <button\n role=\"switch\"\n aria-checked={checked}\n onClick={() => onChange(!checked)}\n style={{\n position: 'relative',\n display: 'inline-flex',\n alignItems: 'center',\n width: 44,\n height: 24,\n borderRadius: 9999,\n backgroundColor: checked ? 'var(--voice-settings-accent, #DB2129)' : '#d1d5db',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n transition: 'background-color 0.2s',\n flexShrink: 0,\n }}\n >\n <span\n style={{\n display: 'inline-block',\n width: 20,\n height: 20,\n borderRadius: 9999,\n backgroundColor: '#fff',\n boxShadow: '0 1px 3px rgba(0,0,0,0.2)',\n transition: 'transform 0.2s',\n transform: checked ? 'translateX(22px)' : 'translateX(2px)',\n }}\n />\n </button>\n </label>\n );\n}\n\n/** Select setting row */\nexport function SelectSetting({\n icon,\n label,\n value,\n onChange,\n options,\n}: {\n icon: React.ReactNode;\n label: string;\n value: string;\n onChange: (v: string) => void;\n options: { value: string; label: string }[];\n}) {\n return (\n <div style={{ paddingTop: 12, paddingBottom: 12, display: 'flex', alignItems: 'center', gap: 12 }}>\n {icon}\n <span style={{ flex: 1, fontSize: 13, fontWeight: 500, color: '#111827' }}>{label}</span>\n <select\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onMouseEnter={(e) => {\n e.currentTarget.style.borderColor = '#d1d5db';\n e.currentTarget.style.backgroundColor = '#f3f4f6';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.borderColor = '#e5e7eb';\n e.currentTarget.style.backgroundColor = '#f9fafb';\n }}\n onFocus={(e) => { e.currentTarget.style.borderColor = '#9ca3af'; }}\n onBlur={(e) => { e.currentTarget.style.borderColor = '#e5e7eb'; }}\n style={{\n height: 32,\n fontSize: 12,\n fontWeight: 500,\n color: '#374151',\n borderRadius: 9999,\n border: '1px solid #e5e7eb',\n backgroundColor: '#f9fafb',\n paddingLeft: 12,\n paddingRight: 28,\n outline: 'none',\n WebkitAppearance: 'none',\n appearance: 'none',\n cursor: 'pointer',\n transition: 'border-color 0.15s, background-color 0.15s',\n 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\")`,\n backgroundRepeat: 'no-repeat',\n backgroundPosition: 'right 8px center',\n }}\n >\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </div>\n );\n}\n\nexport default function VoiceSettingsView({ onBack, onVolumeChange }: VoiceSettingsViewProps) {\n const { settings, updateSetting, resetSettings } = useVoiceSettings();\n const config = useSiteConfig();\n const { colors } = config;\n const [openSection, setOpenSection] = useState<string | null>(null);\n const iconStyle = { width: 16, height: 16, flexShrink: 0, color: colors.primary };\n const sectionIconStyle = { width: 16, height: 16, flexShrink: 0, color: colors.primary };\n const sectionProps = (id: string) => ({\n open: openSection === id,\n onToggle: () => setOpenSection(openSection === id ? null : id),\n });\n\n return (\n <motion.div\n initial={{ opacity: 0, x: 20 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: 20 }}\n transition={{ duration: 0.2, ease: [0.22, 1, 0.36, 1] }}\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n zIndex: 10,\n borderRadius: 'inherit',\n backgroundColor: '#f9fafb',\n fontFamily: 'inherit',\n '--voice-settings-accent': colors.primary,\n } as React.CSSProperties}\n >\n {/* Header */}\n <div\n style={{ display: 'flex', alignItems: 'center', gap: 10, flexShrink: 0, paddingLeft: 16, paddingRight: 16, height: 56, borderBottom: '1px solid #e5e7eb' }}\n >\n <button\n onClick={onBack}\n style={{ width: 32, height: 32, borderRadius: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', transition: 'color 0.15s', color: '#6b7280', background: 'none', border: 'none' }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.color = '#111827';\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.color = '#6b7280';\n }}\n aria-label=\"Back to conversation\"\n >\n <ArrowLeft style={{ width: 16, height: 16 }} />\n </button>\n <span style={{ flex: 1, fontSize: 13, fontWeight: 600, color: '#111827' }}>\n Settings\n </span>\n <button\n onClick={resetSettings}\n style={{ width: 32, height: 32, borderRadius: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', transition: 'color 0.15s', color: '#9ca3af', background: 'none', border: 'none' }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.color = colors.primary;\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.color = '#9ca3af';\n }}\n aria-label=\"Reset all settings\"\n title=\"Reset to defaults\"\n >\n <RotateCcw style={{ width: 14, height: 14 }} />\n </button>\n </div>\n\n {/* Scrollable content */}\n <div style={{ flex: 1, minHeight: 0, overflowY: 'auto' }}>\n {/* Agent Persona */}\n {config.personaEndpoint && (\n <SettingsSection title=\"Persona\" icon={<User style={sectionIconStyle} />} {...sectionProps('persona')}>\n <PersonaSettings />\n </SettingsSection>\n )}\n\n {/* Conversation */}\n <SettingsSection title=\"Conversation\" icon={<MessageCircle style={sectionIconStyle} />} {...sectionProps('conversation')}>\n <SelectSetting\n icon={<MessageSquare style={iconStyle} />}\n label=\"Response length\"\n value={String(settings.responseLength)}\n onChange={(v) => updateSetting('responseLength', Number(v))}\n options={[\n { value: '30', label: 'Brief' },\n { value: '60', label: 'Normal' },\n { value: '100', label: 'Detailed' },\n ]}\n />\n <Divider />\n <SelectSetting\n icon={<History style={iconStyle} />}\n label=\"Chat memory\"\n value={String(settings.maxHistoryMessages)}\n onChange={(v) => updateSetting('maxHistoryMessages', Number(v))}\n options={[\n { value: '10', label: '10 msgs' },\n { value: '20', label: '20 msgs' },\n { value: '30', label: '30 msgs' },\n { value: '40', label: '40 msgs' },\n ]}\n />\n </SettingsSection>\n\n {/* Listening */}\n <SettingsSection title=\"Listening\" icon={<Headphones style={sectionIconStyle} />} {...sectionProps('listening')}>\n <SelectSetting\n icon={<Globe style={iconStyle} />}\n label=\"Language\"\n value={settings.language}\n onChange={(v) => updateSetting('language', v)}\n options={LANGUAGE_OPTIONS}\n />\n <Divider />\n <ToggleSetting\n icon={<Mic style={iconStyle} />}\n label=\"Auto-listen\"\n description=\"Start mic when panel opens\"\n checked={settings.autoListen}\n onChange={(v) => updateSetting('autoListen', v)}\n />\n <Divider />\n <SliderSetting\n icon={<Ear style={iconStyle} />}\n label=\"Speech threshold\"\n value={settings.speechThreshold * 100}\n displayValue={speechThresholdLabel(settings.speechThreshold)}\n min={30}\n max={90}\n step={5}\n onChange={(v) => updateSetting('speechThreshold', v / 100)}\n />\n <Divider />\n <SelectSetting\n icon={<Clock style={iconStyle} />}\n label=\"Pause tolerance\"\n value={String(settings.pauseToleranceMs)}\n onChange={(v) => updateSetting('pauseToleranceMs', Number(v))}\n options={[\n { value: '400', label: 'Fast' },\n { value: '600', label: 'Default' },\n { value: '800', label: 'Relaxed' },\n { value: '1000', label: 'Patient' },\n ]}\n />\n <Divider />\n <SliderSetting\n icon={<Zap style={iconStyle} />}\n label=\"Barge-in threshold\"\n value={settings.bargeInThreshold * 100}\n displayValue={bargeInLabel(settings.bargeInThreshold)}\n min={40}\n max={90}\n step={5}\n onChange={(v) => updateSetting('bargeInThreshold', v / 100)}\n />\n </SettingsSection>\n\n {/* Speaking */}\n <SettingsSection title=\"Speaking\" icon={<Volume2 style={sectionIconStyle} />} {...sectionProps('speaking')}>\n <ToggleSetting\n icon={<AudioLines style={iconStyle} />}\n label=\"Text-to-speech\"\n description=\"Speak responses aloud\"\n checked={settings.ttsEnabled}\n onChange={(v) => updateSetting('ttsEnabled', v)}\n />\n <Divider />\n <SliderSetting\n icon={<Volume2 style={iconStyle} />}\n label=\"Volume\"\n value={settings.volume * 100}\n displayValue={`${Math.round(settings.volume * 100)}%`}\n min={0}\n max={100}\n step={1}\n onChange={(v) => {\n updateSetting('volume', v / 100);\n onVolumeChange?.(v / 100);\n }}\n />\n <Divider />\n <SliderSetting\n icon={<Gauge style={iconStyle} />}\n label=\"Speed\"\n value={settings.playbackSpeed * 100}\n displayValue={`${settings.playbackSpeed.toFixed(2)}x`}\n min={75}\n max={150}\n step={5}\n onChange={(v) => updateSetting('playbackSpeed', v / 100)}\n />\n <Divider />\n <SliderSetting\n icon={<Sparkles style={iconStyle} />}\n label=\"Expressiveness\"\n value={settings.expressiveness * 100}\n displayValue={expressivenessLabel(settings.expressiveness)}\n min={10}\n max={60}\n step={5}\n onChange={(v) => updateSetting('expressiveness', v / 100)}\n />\n </SettingsSection>\n\n {/* Behavior */}\n <SettingsSection title=\"Behavior\" icon={<SlidersHorizontal style={sectionIconStyle} />} {...sectionProps('behavior')}>\n <SelectSetting\n icon={<Timer style={iconStyle} />}\n label=\"Idle timeout\"\n value={String(settings.idleTimeoutMs)}\n onChange={(v) => updateSetting('idleTimeoutMs', Number(v))}\n options={[\n { value: '30000', label: '30s' },\n { value: '60000', label: '1 min' },\n { value: '120000', label: '2 min' },\n { value: '300000', label: '5 min' },\n ]}\n />\n <Divider />\n <SelectSetting\n icon={<Minimize2 style={iconStyle} />}\n label=\"Auto-collapse\"\n value={String(settings.panelCollapseTimeoutMs)}\n onChange={(v) => updateSetting('panelCollapseTimeoutMs', Number(v))}\n options={[\n { value: '120000', label: '2 min' },\n { value: '300000', label: '5 min' },\n { value: '600000', label: '10 min' },\n { value: '0', label: 'Never' },\n ]}\n />\n </SettingsSection>\n\n {/* Developer */}\n <SettingsSection title=\"Developer\" icon={<Wrench style={sectionIconStyle} />} {...sectionProps('developer')} last>\n <ToggleSetting\n icon={<Activity style={iconStyle} />}\n label=\"Pipeline metrics\"\n description=\"Show STT / LLM / TTS timings\"\n checked={settings.showPipelineMetrics}\n onChange={(v) => updateSetting('showPipelineMetrics', v)}\n />\n <Divider />\n <SelectSetting\n icon={<EyeOff style={iconStyle} />}\n label=\"Auto-hide metrics\"\n value={String(settings.pipelineMetricsAutoHideMs)}\n onChange={(v) => updateSetting('pipelineMetricsAutoHideMs', Number(v))}\n options={[\n { value: '5000', label: '5s' },\n { value: '8000', label: '8s' },\n { value: '15000', label: '15s' },\n { value: '0', label: 'Never' },\n ]}\n />\n <Divider />\n <SelectSetting\n icon={<Mic style={iconStyle} />}\n label=\"STT timeout\"\n value={String(settings.sttTimeoutMs)}\n onChange={(v) => updateSetting('sttTimeoutMs', Number(v))}\n options={[\n { value: '10000', label: '10s' },\n { value: '15000', label: '15s' },\n { value: '30000', label: '30s' },\n ]}\n />\n <Divider />\n <SelectSetting\n icon={<AudioLines style={iconStyle} />}\n label=\"TTS timeout\"\n value={String(settings.ttsTimeoutMs)}\n onChange={(v) => updateSetting('ttsTimeoutMs', Number(v))}\n options={[\n { value: '30000', label: '30s' },\n { value: '55000', label: '55s' },\n { value: '90000', label: '90s' },\n ]}\n />\n <Divider />\n <SelectSetting\n icon={<Cpu style={iconStyle} />}\n label=\"LLM timeout\"\n value={String(settings.llmTimeoutMs)}\n onChange={(v) => updateSetting('llmTimeoutMs', Number(v))}\n options={[\n { value: '10000', label: '10s' },\n { value: '20000', label: '20s' },\n { value: '30000', label: '30s' },\n { value: '60000', label: '60s' },\n ]}\n />\n <Divider />\n <SelectSetting\n icon={<Signal style={iconStyle} />}\n label=\"Min audio level\"\n value={String(settings.minAudioRms)}\n onChange={(v) => updateSetting('minAudioRms', Number(v))}\n options={[\n { value: '0.01', label: 'Sensitive' },\n { value: '0.02', label: 'Default' },\n { value: '0.035', label: 'Moderate' },\n { value: '0.05', label: 'Strict' },\n ]}\n />\n <Divider />\n <div style={{ paddingTop: 8, paddingBottom: 4, fontSize: 11, color: '#9ca3af' }}>\n VAD Threshold: <span style={{ fontWeight: 500, color: '#6b7280' }}>{VAD.positiveSpeechThreshold}</span>\n </div>\n <div style={{ paddingTop: 2, paddingBottom: 4, fontSize: 11, color: '#9ca3af' }}>\n Kit version: <span style={{ fontWeight: 500, color: '#6b7280' }}>{__KIT_VERSION__}</span>\n </div>\n </SettingsSection>\n </div>\n </motion.div>\n );\n}\n\nexport function SettingsSection({\n title,\n icon,\n children,\n last,\n open = false,\n onToggle,\n}: {\n title: string;\n icon?: React.ReactNode;\n children: React.ReactNode;\n last?: boolean;\n open?: boolean;\n onToggle?: () => void;\n}) {\n const [hovered, setHovered] = useState(false);\n\n return (\n <div style={{ borderBottom: last ? 'none' : '1px solid #e5e7eb' }}>\n <button\n onClick={onToggle}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{\n width: '100%',\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n paddingLeft: 16,\n paddingRight: 16,\n paddingTop: 14,\n paddingBottom: 14,\n backgroundColor: hovered ? '#edf0f3' : open ? '#f0f2f5' : 'transparent',\n border: 'none',\n cursor: 'pointer',\n fontFamily: 'inherit',\n transition: 'background-color 0.15s',\n }}\n >\n {icon}\n <span style={{\n flex: 1,\n textAlign: 'left',\n fontSize: 13,\n fontWeight: 600,\n color: '#374151',\n }}>{title}</span>\n <ChevronDown style={{\n width: 14,\n height: 14,\n color: '#9ca3af',\n transition: 'transform 0.2s ease',\n transform: open ? 'rotate(0deg)' : 'rotate(-90deg)',\n }} />\n </button>\n <AnimatePresence initial={false}>\n {open && (\n <motion.div\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: 0.2, ease: [0.22, 1, 0.36, 1] }}\n style={{ overflow: 'hidden' }}\n >\n <div style={{\n paddingLeft: 16,\n paddingRight: 16,\n paddingTop: 4,\n paddingBottom: 12,\n backgroundColor: '#fff',\n borderTop: '1px solid #e5e7eb',\n }}>\n {children}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n\nexport function Divider() {\n return <div style={{ height: 1, backgroundColor: '#f3f4f6' }} />;\n}\n","import { createContext, useContext, useState, useRef, useCallback, type ReactNode } from 'react';\nimport {\n DEFAULT_VOLUME,\n DEFAULT_PLAYBACK_SPEED,\n DEFAULT_TTS_ENABLED,\n DEFAULT_AUTO_LISTEN,\n DEFAULT_IDLE_TIMEOUT_MS,\n DEFAULT_EXPRESSIVENESS,\n DEFAULT_RESPONSE_LENGTH,\n DEFAULT_SHOW_PIPELINE_METRICS,\n DEFAULT_PIPELINE_METRICS_AUTO_HIDE_MS,\n DEFAULT_SPEECH_THRESHOLD,\n DEFAULT_PAUSE_TOLERANCE_MS,\n DEFAULT_BARGE_IN_THRESHOLD,\n DEFAULT_PANEL_COLLAPSE_TIMEOUT_MS,\n DEFAULT_STT_TIMEOUT_MS,\n DEFAULT_TTS_TIMEOUT_MS,\n DEFAULT_LLM_TIMEOUT_MS,\n DEFAULT_MIN_AUDIO_RMS,\n DEFAULT_MAX_HISTORY_MESSAGES,\n DEFAULT_LANGUAGE,\n} from '@unctad-ai/voice-agent-core';\n\nexport interface VoiceSettings {\n volume: number;\n playbackSpeed: number;\n ttsEnabled: boolean;\n autoListen: boolean;\n idleTimeoutMs: number;\n expressiveness: number;\n responseLength: number;\n showPipelineMetrics: boolean;\n pipelineMetricsAutoHideMs: number;\n speechThreshold: number;\n pauseToleranceMs: number;\n bargeInThreshold: number;\n panelCollapseTimeoutMs: number;\n sttTimeoutMs: number;\n ttsTimeoutMs: number;\n llmTimeoutMs: number;\n minAudioRms: number;\n maxHistoryMessages: number;\n language: string;\n}\n\nconst DEFAULTS: VoiceSettings = {\n volume: DEFAULT_VOLUME,\n playbackSpeed: DEFAULT_PLAYBACK_SPEED,\n ttsEnabled: DEFAULT_TTS_ENABLED,\n autoListen: DEFAULT_AUTO_LISTEN,\n idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,\n expressiveness: DEFAULT_EXPRESSIVENESS,\n responseLength: DEFAULT_RESPONSE_LENGTH,\n showPipelineMetrics: DEFAULT_SHOW_PIPELINE_METRICS,\n pipelineMetricsAutoHideMs: DEFAULT_PIPELINE_METRICS_AUTO_HIDE_MS,\n speechThreshold: DEFAULT_SPEECH_THRESHOLD,\n pauseToleranceMs: DEFAULT_PAUSE_TOLERANCE_MS,\n bargeInThreshold: DEFAULT_BARGE_IN_THRESHOLD,\n panelCollapseTimeoutMs: DEFAULT_PANEL_COLLAPSE_TIMEOUT_MS,\n sttTimeoutMs: DEFAULT_STT_TIMEOUT_MS,\n ttsTimeoutMs: DEFAULT_TTS_TIMEOUT_MS,\n llmTimeoutMs: DEFAULT_LLM_TIMEOUT_MS,\n minAudioRms: DEFAULT_MIN_AUDIO_RMS,\n maxHistoryMessages: DEFAULT_MAX_HISTORY_MESSAGES,\n language: DEFAULT_LANGUAGE,\n};\n\nconst STORAGE_KEY = 'voice-settings';\n\nfunction loadSettings(): VoiceSettings {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return { ...DEFAULTS };\n const parsed = JSON.parse(raw);\n // Merge with defaults to handle missing keys from older versions\n return { ...DEFAULTS, ...parsed };\n } catch {\n return { ...DEFAULTS };\n }\n}\n\nfunction persistSettings(settings: VoiceSettings) {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));\n } catch {\n // localStorage full or unavailable — silently skip\n }\n}\n\ninterface VoiceSettingsContextType {\n settings: VoiceSettings;\n volumeRef: React.RefObject<number>;\n speedRef: React.RefObject<number>;\n updateSetting: <K extends keyof VoiceSettings>(key: K, value: VoiceSettings[K]) => void;\n resetSettings: () => void;\n}\n\nconst VoiceSettingsContext = createContext<VoiceSettingsContextType | undefined>(undefined);\n\ninterface VoiceSettingsProviderProps {\n children: ReactNode;\n siteLanguage?: string;\n}\n\nexport function VoiceSettingsProvider({ children, siteLanguage }: VoiceSettingsProviderProps) {\n const [settings, setSettings] = useState<VoiceSettings>(() => {\n const loaded = loadSettings();\n if (siteLanguage) {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n const hasPersisted = raw ? Object.prototype.hasOwnProperty.call(JSON.parse(raw), 'language') : false;\n if (!hasPersisted) loaded.language = siteLanguage;\n } catch {\n loaded.language = siteLanguage;\n }\n }\n return loaded;\n });\n\n // Refs for hot-path audio — avoids re-renders on volume/speed drag\n const volumeRef = useRef(settings.volume);\n const speedRef = useRef(settings.playbackSpeed);\n const siteLanguageRef = useRef(siteLanguage);\n siteLanguageRef.current = siteLanguage;\n\n const updateSetting = useCallback(\n <K extends keyof VoiceSettings>(key: K, value: VoiceSettings[K]) => {\n setSettings((prev) => {\n const next = { ...prev, [key]: value };\n persistSettings(next);\n // Keep refs in sync\n if (key === 'volume') volumeRef.current = value as number;\n if (key === 'playbackSpeed') speedRef.current = value as number;\n return next;\n });\n },\n []\n );\n\n const resetSettings = useCallback(() => {\n const defaults = { ...DEFAULTS, language: siteLanguageRef.current ?? DEFAULT_LANGUAGE };\n setSettings(defaults);\n persistSettings(defaults);\n volumeRef.current = defaults.volume;\n speedRef.current = defaults.playbackSpeed;\n }, []);\n\n return (\n <VoiceSettingsContext.Provider\n value={{ settings, volumeRef, speedRef, updateSetting, resetSettings }}\n >\n {children}\n </VoiceSettingsContext.Provider>\n );\n}\n\n/**\n * Access voice settings. Returns defaults if Provider is absent\n * (needed for error boundary paths where the tree may be partial).\n */\nexport function useVoiceSettings(): VoiceSettingsContextType {\n const context = useContext(VoiceSettingsContext);\n if (context) return context;\n\n // Fallback — return static defaults (no persistence, no-op updates)\n return {\n settings: DEFAULTS,\n volumeRef: { current: DEFAULTS.volume },\n speedRef: { current: DEFAULTS.playbackSpeed },\n updateSetting: () => {},\n resetSettings: () => {},\n };\n}\n","import { useState, useRef, useEffect } from 'react';\nimport { usePersonaContext, useSiteConfig } from '@unctad-ai/voice-agent-core';\n\nexport function PersonaSettings() {\n const config = useSiteConfig();\n if (!config.personaEndpoint) return null;\n\n return <PersonaSettingsInner />;\n}\n\nfunction PersonaSettingsInner() {\n const config = useSiteConfig();\n const persona = usePersonaContext();\n if (!persona) return null;\n const { persona: data, isLoaded, updateName, uploadAvatar, uploadVoice, deleteVoice, setActiveVoice, previewVoice } = persona;\n\n if (!isLoaded) {\n return (\n <div style={{ padding: 16, fontSize: 13, color: '#9ca3af', fontFamily: 'inherit' }}>\n Loading persona settings...\n </div>\n );\n }\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12, fontFamily: 'inherit' }}>\n <AvatarSection avatarUrl={config.avatarUrl} name={config.copilotName} onUpload={uploadAvatar} />\n <NameSection name={config.copilotName} onSave={updateName} primaryColor={config.colors.primary} />\n <VoiceSection\n voices={data?.voices ?? []}\n activeVoiceId={data?.activeVoiceId ?? ''}\n onUpload={uploadVoice}\n onDelete={deleteVoice}\n onSelect={setActiveVoice}\n onPreview={previewVoice}\n primaryColor={config.colors.primary}\n />\n </div>\n );\n}\n\nfunction AvatarSection({ avatarUrl, name, onUpload }: {\n avatarUrl?: string;\n name: string;\n onUpload: (file: File) => Promise<void>;\n}) {\n const [uploading, setUploading] = useState(false);\n const [hovered, setHovered] = useState(false);\n const [imgError, setImgError] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const initial = (name || '?')[0].toUpperCase();\n\n const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n setUploading(true);\n try { await onUpload(file); setImgError(false); }\n catch (err) { console.error('Avatar upload failed:', err); }\n finally { setUploading(false); }\n };\n\n const showImage = avatarUrl && !imgError;\n\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: 12, paddingTop: 4, paddingBottom: 4 }}>\n <div\n onClick={() => !uploading && inputRef.current?.click()}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{\n position: 'relative',\n width: 44,\n height: 44,\n borderRadius: 9999,\n overflow: 'hidden',\n backgroundColor: '#e5e7eb',\n flexShrink: 0,\n cursor: uploading ? 'wait' : 'pointer',\n }}\n >\n {showImage ? (\n <img\n src={avatarUrl}\n alt=\"\"\n onError={() => setImgError(true)}\n style={{ width: '100%', height: '100%', objectFit: 'cover' }}\n />\n ) : (\n <div style={{\n width: '100%',\n height: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#6b7280',\n fontSize: 16,\n fontWeight: 600,\n }}>{initial}</div>\n )}\n {/* hover overlay */}\n {hovered && !uploading && (\n <div style={{\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0,0,0,0.35)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#fff',\n fontSize: 9,\n fontWeight: 600,\n letterSpacing: '0.02em',\n }}>Edit</div>\n )}\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 13, fontWeight: 500, color: '#111827' }}>Avatar</div>\n <div style={{ fontSize: 11, color: '#6b7280' }}>\n {uploading ? 'Uploading...' : 'Click to change'}\n </div>\n </div>\n <input ref={inputRef} type=\"file\" accept=\"image/png,image/jpeg,image/webp\" onChange={handleFile} style={{ display: 'none' }} />\n </div>\n );\n}\n\nfunction NameSection({ name, onSave, primaryColor }: {\n name: string;\n onSave: (name: string) => Promise<void>;\n primaryColor: string;\n}) {\n const [value, setValue] = useState(name);\n const [saving, setSaving] = useState(false);\n const [inputFocused, setInputFocused] = useState(false);\n const [saveHovered, setSaveHovered] = useState(false);\n const dirty = value !== name;\n\n useEffect(() => { setValue(name); }, [name]);\n\n const handleSave = async () => {\n setSaving(true);\n try { await onSave(value); }\n catch (err) { console.error('Name update failed:', err); }\n finally { setSaving(false); }\n };\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 6, paddingTop: 4, paddingBottom: 4 }}>\n <span style={{ fontSize: 13, fontWeight: 500, color: '#111827' }}>Name</span>\n <div style={{ display: 'flex', gap: 8 }}>\n <input\n type=\"text\"\n value={value}\n onChange={e => setValue(e.target.value)}\n onFocus={() => setInputFocused(true)}\n onBlur={() => setInputFocused(false)}\n maxLength={30}\n style={{\n flex: 1,\n fontSize: 13,\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 6,\n paddingBottom: 6,\n borderRadius: 8,\n border: `1px solid ${inputFocused ? '#9ca3af' : '#e5e7eb'}`,\n backgroundColor: '#fff',\n outline: 'none',\n fontFamily: 'inherit',\n transition: 'border-color 0.15s',\n }}\n />\n {dirty && (\n <button\n onClick={handleSave}\n disabled={saving}\n onMouseEnter={() => setSaveHovered(true)}\n onMouseLeave={() => setSaveHovered(false)}\n style={{\n fontSize: 12,\n fontWeight: 500,\n paddingLeft: 12,\n paddingRight: 12,\n paddingTop: 6,\n paddingBottom: 6,\n borderRadius: 8,\n border: 'none',\n backgroundColor: saveHovered ? primaryColor : '#1f2937',\n color: '#fff',\n cursor: saving ? 'default' : 'pointer',\n opacity: saving ? 0.5 : 1,\n transition: 'background-color 0.15s',\n fontFamily: 'inherit',\n }}\n >\n {saving ? 'Saving...' : 'Save'}\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onPreview, primaryColor }: {\n voices: { id: string; name: string }[];\n activeVoiceId: string;\n onUpload: (file: File, name: string) => Promise<any>;\n onDelete: (id: string) => Promise<void>;\n onSelect: (id: string) => Promise<void>;\n onPreview: (id: string, text: string) => Promise<ArrayBuffer>;\n primaryColor: string;\n}) {\n const [uploading, setUploading] = useState(false);\n const [uploadName, setUploadName] = useState('');\n const [showUpload, setShowUpload] = useState(false);\n const [previewing, setPreviewing] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const fileRef = useRef<File | null>(null);\n\n const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n fileRef.current = file;\n setUploadName(file.name.replace(/\\.wav$/i, ''));\n setShowUpload(true);\n };\n\n const handleUpload = async () => {\n if (!fileRef.current || !uploadName) return;\n setUploading(true);\n try {\n await onUpload(fileRef.current, uploadName);\n setShowUpload(false);\n setUploadName('');\n fileRef.current = null;\n } catch (err) {\n console.error('Voice upload failed:', err);\n } finally {\n setUploading(false);\n }\n };\n\n const handlePreview = async (voiceId: string) => {\n setPreviewing(voiceId);\n try {\n const buffer = await onPreview(voiceId, 'Hello, I am your AI assistant. How can I help you today?');\n const blob = new Blob([buffer], { type: 'audio/wav' });\n const url = URL.createObjectURL(blob);\n const audio = new Audio(url);\n audio.onended = () => { URL.revokeObjectURL(url); setPreviewing(null); };\n audio.play();\n } catch (err) {\n console.error('Preview failed:', err);\n setPreviewing(null);\n }\n };\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8, paddingTop: 4, paddingBottom: 4 }}>\n <span style={{ fontSize: 13, fontWeight: 500, color: '#111827' }}>Voice</span>\n\n {voices.length === 0 && (\n <p style={{ fontSize: 11, color: '#9ca3af', margin: 0 }}>\n No voices configured. Upload a WAV sample to enable voice cloning.\n </p>\n )}\n\n {voices.map(v => (\n <VoiceRow\n key={v.id}\n voice={v}\n isActive={v.id === activeVoiceId}\n isPreviewing={previewing === v.id}\n onSelect={() => onSelect(v.id)}\n onPreview={() => handlePreview(v.id)}\n onDelete={() => onDelete(v.id)}\n primaryColor={primaryColor}\n />\n ))}\n\n {showUpload ? (\n <UploadForm\n uploadName={uploadName}\n uploading={uploading}\n onNameChange={setUploadName}\n onUpload={handleUpload}\n onCancel={() => { setShowUpload(false); fileRef.current = null; }}\n primaryColor={primaryColor}\n />\n ) : (\n <UploadButton\n disabled={voices.length >= 10}\n onClick={() => inputRef.current?.click()}\n />\n )}\n\n <input ref={inputRef} type=\"file\" accept=\"audio/wav\" onChange={handleFileSelect} style={{ display: 'none' }} />\n </div>\n );\n}\n\nfunction VoiceRow({ voice, isActive, isPreviewing, onSelect, onPreview, onDelete, primaryColor }: {\n voice: { id: string; name: string };\n isActive: boolean;\n isPreviewing: boolean;\n onSelect: () => void;\n onPreview: () => void;\n onDelete: () => void;\n primaryColor: string;\n}) {\n const [previewHovered, setPreviewHovered] = useState(false);\n const [deleteHovered, setDeleteHovered] = useState(false);\n\n return (\n <div style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 8,\n paddingBottom: 8,\n borderRadius: 8,\n border: `1px solid ${isActive ? '#9ca3af' : '#e5e7eb'}`,\n backgroundColor: isActive ? '#f9fafb' : '#fff',\n fontSize: 13,\n }}>\n <input\n type=\"radio\"\n name=\"active-voice\"\n checked={isActive}\n onChange={onSelect}\n style={{ accentColor: primaryColor }}\n />\n <span style={{ flex: 1 }}>{voice.name}</span>\n <button\n onClick={onPreview}\n disabled={isPreviewing}\n onMouseEnter={() => setPreviewHovered(true)}\n onMouseLeave={() => setPreviewHovered(false)}\n style={{\n fontSize: 11,\n color: previewHovered ? '#374151' : '#6b7280',\n background: 'none',\n border: 'none',\n cursor: isPreviewing ? 'default' : 'pointer',\n opacity: isPreviewing ? 0.5 : 1,\n fontFamily: 'inherit',\n transition: 'color 0.15s',\n }}\n >\n {isPreviewing ? 'Playing...' : 'Preview'}\n </button>\n <button\n onClick={onDelete}\n onMouseEnter={() => setDeleteHovered(true)}\n onMouseLeave={() => setDeleteHovered(false)}\n style={{\n fontSize: 11,\n color: deleteHovered ? '#b91c1c' : '#ef4444',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontFamily: 'inherit',\n transition: 'color 0.15s',\n }}\n >\n Delete\n </button>\n </div>\n );\n}\n\nfunction UploadForm({ uploadName, uploading, onNameChange, onUpload, onCancel, primaryColor }: {\n uploadName: string;\n uploading: boolean;\n onNameChange: (v: string) => void;\n onUpload: () => void;\n onCancel: () => void;\n primaryColor: string;\n}) {\n const [uploadHovered, setUploadHovered] = useState(false);\n const [cancelHovered, setCancelHovered] = useState(false);\n\n return (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: 10,\n borderRadius: 6,\n border: '1px solid #e5e7eb',\n backgroundColor: '#f9fafb',\n }}>\n <input\n type=\"text\"\n value={uploadName}\n onChange={e => onNameChange(e.target.value)}\n placeholder=\"Voice name\"\n style={{\n fontSize: 13,\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 6,\n paddingBottom: 6,\n borderRadius: 6,\n border: '1px solid #e5e7eb',\n outline: 'none',\n fontFamily: 'inherit',\n }}\n />\n <div style={{ display: 'flex', gap: 8 }}>\n <button\n onClick={onUpload}\n disabled={uploading || !uploadName}\n onMouseEnter={() => setUploadHovered(true)}\n onMouseLeave={() => setUploadHovered(false)}\n style={{\n fontSize: 11,\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 4,\n paddingBottom: 4,\n borderRadius: 6,\n border: 'none',\n backgroundColor: uploadHovered ? primaryColor : '#1f2937',\n color: '#fff',\n cursor: uploading || !uploadName ? 'default' : 'pointer',\n opacity: uploading || !uploadName ? 0.5 : 1,\n fontFamily: 'inherit',\n transition: 'background-color 0.15s',\n }}\n >\n {uploading ? 'Processing (~8s)...' : 'Upload'}\n </button>\n <button\n onClick={onCancel}\n onMouseEnter={() => setCancelHovered(true)}\n onMouseLeave={() => setCancelHovered(false)}\n style={{\n fontSize: 11,\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 4,\n paddingBottom: 4,\n borderRadius: 6,\n border: '1px solid #e5e7eb',\n backgroundColor: cancelHovered ? '#f9fafb' : 'transparent',\n cursor: 'pointer',\n fontFamily: 'inherit',\n transition: 'background-color 0.15s',\n }}\n >\n Cancel\n </button>\n </div>\n </div>\n );\n}\n\nfunction UploadButton({ disabled, onClick }: { disabled: boolean; onClick: () => void }) {\n const [hovered, setHovered] = useState(false);\n\n return (\n <button\n onClick={onClick}\n disabled={disabled}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{\n fontSize: 11,\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 6,\n paddingBottom: 6,\n borderRadius: 8,\n border: '1px dashed #d1d5db',\n backgroundColor: hovered && !disabled ? '#f9fafb' : '#fff',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n transition: 'background-color 0.15s',\n fontFamily: 'inherit',\n }}\n >\n + Upload voice sample (WAV, max 30s)\n </button>\n );\n}\n"],"mappings":"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","names":["useState","useSiteConfig","useState","useRef","jsx","jsx","jsxs","useSiteConfig","useState"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ interface VoiceSettings {
|
|
|
28
28
|
llmTimeoutMs: number;
|
|
29
29
|
minAudioRms: number;
|
|
30
30
|
maxHistoryMessages: number;
|
|
31
|
+
language: string;
|
|
31
32
|
}
|
|
32
33
|
interface VoiceSettingsContextType {
|
|
33
34
|
settings: VoiceSettings;
|
|
@@ -36,9 +37,11 @@ interface VoiceSettingsContextType {
|
|
|
36
37
|
updateSetting: <K extends keyof VoiceSettings>(key: K, value: VoiceSettings[K]) => void;
|
|
37
38
|
resetSettings: () => void;
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
interface VoiceSettingsProviderProps {
|
|
40
41
|
children: ReactNode;
|
|
41
|
-
|
|
42
|
+
siteLanguage?: string;
|
|
43
|
+
}
|
|
44
|
+
declare function VoiceSettingsProvider({ children, siteLanguage }: VoiceSettingsProviderProps): react_jsx_runtime.JSX.Element;
|
|
42
45
|
/**
|
|
43
46
|
* Access voice settings. Returns defaults if Provider is absent
|
|
44
47
|
* (needed for error boundary paths where the tree may be partial).
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
VoiceSettingsProvider,
|
|
9
9
|
VoiceSettingsView,
|
|
10
10
|
useVoiceSettings
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-YCJGLETL.js";
|
|
12
12
|
|
|
13
13
|
// src/VoiceAgentProvider.tsx
|
|
14
14
|
import { SiteConfigProvider } from "@unctad-ai/voice-agent-core";
|
|
@@ -18,7 +18,7 @@ function VoiceAgentProvider({
|
|
|
18
18
|
config,
|
|
19
19
|
children
|
|
20
20
|
}) {
|
|
21
|
-
return /* @__PURE__ */ jsx(SiteConfigProvider, { config, children: /* @__PURE__ */ jsx(CopilotProvider, { children: /* @__PURE__ */ jsx(VoiceSettingsProvider, { children }) }) });
|
|
21
|
+
return /* @__PURE__ */ jsx(SiteConfigProvider, { config, children: /* @__PURE__ */ jsx(CopilotProvider, { children: /* @__PURE__ */ jsx(VoiceSettingsProvider, { siteLanguage: config.language, children }) }) });
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// src/components/GlassCopilotPanel.tsx
|
|
@@ -1665,7 +1665,7 @@ function PipelineMetricsBar({
|
|
|
1665
1665
|
|
|
1666
1666
|
// src/components/GlassCopilotPanel.tsx
|
|
1667
1667
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1668
|
-
var VoiceSettingsView2 = lazy(() => import("./VoiceSettingsView-
|
|
1668
|
+
var VoiceSettingsView2 = lazy(() => import("./VoiceSettingsView-PXEMZLVK.js"));
|
|
1669
1669
|
var STATE_LABELS = {
|
|
1670
1670
|
IDLE: "Tap mic to speak",
|
|
1671
1671
|
LISTENING: "Listening...",
|