@unctad-ai/voice-agent-ui 0.1.1 → 0.1.2
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-RP5CUCKI.js +7 -0
- package/dist/VoiceSettingsView-RP5CUCKI.js.map +1 -0
- package/dist/chunk-MUFUZ3TH.js +582 -0
- package/dist/chunk-MUFUZ3TH.js.map +1 -0
- package/dist/index.d.ts +184 -22
- package/dist/index.js +3287 -21
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/dist/VoiceAgentProvider.d.ts +0 -6
- package/dist/VoiceAgentProvider.d.ts.map +0 -1
- package/dist/VoiceAgentProvider.js +0 -8
- package/dist/VoiceAgentProvider.js.map +0 -1
- package/dist/components/AgentAvatar.d.ts +0 -16
- package/dist/components/AgentAvatar.d.ts.map +0 -1
- package/dist/components/AgentAvatar.js +0 -186
- package/dist/components/AgentAvatar.js.map +0 -1
- package/dist/components/GlassCopilotPanel.d.ts +0 -13
- package/dist/components/GlassCopilotPanel.d.ts.map +0 -1
- package/dist/components/GlassCopilotPanel.js +0 -438
- package/dist/components/GlassCopilotPanel.js.map +0 -1
- package/dist/components/PipelineMetricsBar.d.ts +0 -7
- package/dist/components/PipelineMetricsBar.d.ts.map +0 -1
- package/dist/components/PipelineMetricsBar.js +0 -62
- package/dist/components/PipelineMetricsBar.js.map +0 -1
- package/dist/components/VoiceA11yAnnouncer.d.ts +0 -8
- package/dist/components/VoiceA11yAnnouncer.d.ts.map +0 -1
- package/dist/components/VoiceA11yAnnouncer.js +0 -32
- package/dist/components/VoiceA11yAnnouncer.js.map +0 -1
- package/dist/components/VoiceControls.d.ts +0 -9
- package/dist/components/VoiceControls.d.ts.map +0 -1
- package/dist/components/VoiceControls.js +0 -60
- package/dist/components/VoiceControls.js.map +0 -1
- package/dist/components/VoiceCopilotFAB.d.ts +0 -9
- package/dist/components/VoiceCopilotFAB.d.ts.map +0 -1
- package/dist/components/VoiceCopilotFAB.js +0 -12
- package/dist/components/VoiceCopilotFAB.js.map +0 -1
- package/dist/components/VoiceDotRing.d.ts +0 -9
- package/dist/components/VoiceDotRing.d.ts.map +0 -1
- package/dist/components/VoiceDotRing.js +0 -64
- package/dist/components/VoiceDotRing.js.map +0 -1
- package/dist/components/VoiceErrorBoundary.d.ts +0 -19
- package/dist/components/VoiceErrorBoundary.d.ts.map +0 -1
- package/dist/components/VoiceErrorBoundary.js +0 -24
- package/dist/components/VoiceErrorBoundary.js.map +0 -1
- package/dist/components/VoiceErrorDisplay.d.ts +0 -8
- package/dist/components/VoiceErrorDisplay.d.ts.map +0 -1
- package/dist/components/VoiceErrorDisplay.js +0 -94
- package/dist/components/VoiceErrorDisplay.js.map +0 -1
- package/dist/components/VoiceOnboarding.d.ts +0 -8
- package/dist/components/VoiceOnboarding.d.ts.map +0 -1
- package/dist/components/VoiceOnboarding.js +0 -28
- package/dist/components/VoiceOnboarding.js.map +0 -1
- package/dist/components/VoiceOrb.d.ts +0 -10
- package/dist/components/VoiceOrb.d.ts.map +0 -1
- package/dist/components/VoiceOrb.js +0 -236
- package/dist/components/VoiceOrb.js.map +0 -1
- package/dist/components/VoiceOverlay.d.ts +0 -9
- package/dist/components/VoiceOverlay.d.ts.map +0 -1
- package/dist/components/VoiceOverlay.js +0 -199
- package/dist/components/VoiceOverlay.js.map +0 -1
- package/dist/components/VoiceSettingsView.d.ts +0 -7
- package/dist/components/VoiceSettingsView.d.ts.map +0 -1
- package/dist/components/VoiceSettingsView.js +0 -109
- package/dist/components/VoiceSettingsView.js.map +0 -1
- package/dist/components/VoiceToolCard.d.ts +0 -11
- package/dist/components/VoiceToolCard.d.ts.map +0 -1
- package/dist/components/VoiceToolCard.js +0 -37
- package/dist/components/VoiceToolCard.js.map +0 -1
- package/dist/components/VoiceTranscript.d.ts +0 -13
- package/dist/components/VoiceTranscript.d.ts.map +0 -1
- package/dist/components/VoiceTranscript.js +0 -313
- package/dist/components/VoiceTranscript.js.map +0 -1
- package/dist/components/VoiceWaveformCanvas.d.ts +0 -9
- package/dist/components/VoiceWaveformCanvas.d.ts.map +0 -1
- package/dist/components/VoiceWaveformCanvas.js +0 -91
- package/dist/components/VoiceWaveformCanvas.js.map +0 -1
- package/dist/contexts/VoiceSettingsContext.d.ts +0 -37
- package/dist/contexts/VoiceSettingsContext.d.ts.map +0 -1
- package/dist/contexts/VoiceSettingsContext.js +0 -89
- package/dist/contexts/VoiceSettingsContext.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/utils.d.ts +0 -3
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -6
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
// src/components/VoiceSettingsView.tsx
|
|
2
|
+
import { motion } from "motion/react";
|
|
3
|
+
import {
|
|
4
|
+
ArrowLeft,
|
|
5
|
+
RotateCcw,
|
|
6
|
+
Volume2,
|
|
7
|
+
Gauge,
|
|
8
|
+
AudioLines,
|
|
9
|
+
Sparkles,
|
|
10
|
+
Mic,
|
|
11
|
+
Timer,
|
|
12
|
+
MessageSquare,
|
|
13
|
+
Activity,
|
|
14
|
+
EyeOff,
|
|
15
|
+
Info,
|
|
16
|
+
Ear,
|
|
17
|
+
Clock,
|
|
18
|
+
Zap,
|
|
19
|
+
Minimize2,
|
|
20
|
+
Signal
|
|
21
|
+
} from "lucide-react";
|
|
22
|
+
|
|
23
|
+
// src/contexts/VoiceSettingsContext.tsx
|
|
24
|
+
import { createContext, useContext, useState, useRef, useCallback } from "react";
|
|
25
|
+
import {
|
|
26
|
+
DEFAULT_VOLUME,
|
|
27
|
+
DEFAULT_PLAYBACK_SPEED,
|
|
28
|
+
DEFAULT_TTS_ENABLED,
|
|
29
|
+
DEFAULT_AUTO_LISTEN,
|
|
30
|
+
DEFAULT_IDLE_TIMEOUT_MS,
|
|
31
|
+
DEFAULT_EXPRESSIVENESS,
|
|
32
|
+
DEFAULT_RESPONSE_LENGTH,
|
|
33
|
+
DEFAULT_SHOW_PIPELINE_METRICS,
|
|
34
|
+
DEFAULT_PIPELINE_METRICS_AUTO_HIDE_MS,
|
|
35
|
+
DEFAULT_SPEECH_THRESHOLD,
|
|
36
|
+
DEFAULT_PAUSE_TOLERANCE_MS,
|
|
37
|
+
DEFAULT_BARGE_IN_THRESHOLD,
|
|
38
|
+
DEFAULT_PANEL_COLLAPSE_TIMEOUT_MS,
|
|
39
|
+
DEFAULT_STT_TIMEOUT_MS,
|
|
40
|
+
DEFAULT_TTS_TIMEOUT_MS,
|
|
41
|
+
DEFAULT_LLM_TIMEOUT_MS,
|
|
42
|
+
DEFAULT_MIN_AUDIO_RMS
|
|
43
|
+
} from "@unctad-ai/voice-agent-core";
|
|
44
|
+
import { jsx } from "react/jsx-runtime";
|
|
45
|
+
var DEFAULTS = {
|
|
46
|
+
volume: DEFAULT_VOLUME,
|
|
47
|
+
playbackSpeed: DEFAULT_PLAYBACK_SPEED,
|
|
48
|
+
ttsEnabled: DEFAULT_TTS_ENABLED,
|
|
49
|
+
autoListen: DEFAULT_AUTO_LISTEN,
|
|
50
|
+
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
51
|
+
expressiveness: DEFAULT_EXPRESSIVENESS,
|
|
52
|
+
responseLength: DEFAULT_RESPONSE_LENGTH,
|
|
53
|
+
showPipelineMetrics: DEFAULT_SHOW_PIPELINE_METRICS,
|
|
54
|
+
pipelineMetricsAutoHideMs: DEFAULT_PIPELINE_METRICS_AUTO_HIDE_MS,
|
|
55
|
+
speechThreshold: DEFAULT_SPEECH_THRESHOLD,
|
|
56
|
+
pauseToleranceMs: DEFAULT_PAUSE_TOLERANCE_MS,
|
|
57
|
+
bargeInThreshold: DEFAULT_BARGE_IN_THRESHOLD,
|
|
58
|
+
panelCollapseTimeoutMs: DEFAULT_PANEL_COLLAPSE_TIMEOUT_MS,
|
|
59
|
+
sttTimeoutMs: DEFAULT_STT_TIMEOUT_MS,
|
|
60
|
+
ttsTimeoutMs: DEFAULT_TTS_TIMEOUT_MS,
|
|
61
|
+
llmTimeoutMs: DEFAULT_LLM_TIMEOUT_MS,
|
|
62
|
+
minAudioRms: DEFAULT_MIN_AUDIO_RMS
|
|
63
|
+
};
|
|
64
|
+
var STORAGE_KEY = "voice-settings";
|
|
65
|
+
function loadSettings() {
|
|
66
|
+
try {
|
|
67
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
68
|
+
if (!raw) return { ...DEFAULTS };
|
|
69
|
+
const parsed = JSON.parse(raw);
|
|
70
|
+
return { ...DEFAULTS, ...parsed };
|
|
71
|
+
} catch {
|
|
72
|
+
return { ...DEFAULTS };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function persistSettings(settings) {
|
|
76
|
+
try {
|
|
77
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
var VoiceSettingsContext = createContext(void 0);
|
|
82
|
+
function VoiceSettingsProvider({ children }) {
|
|
83
|
+
const [settings, setSettings] = useState(loadSettings);
|
|
84
|
+
const volumeRef = useRef(settings.volume);
|
|
85
|
+
const speedRef = useRef(settings.playbackSpeed);
|
|
86
|
+
const updateSetting = useCallback(
|
|
87
|
+
(key, value) => {
|
|
88
|
+
setSettings((prev) => {
|
|
89
|
+
const next = { ...prev, [key]: value };
|
|
90
|
+
persistSettings(next);
|
|
91
|
+
if (key === "volume") volumeRef.current = value;
|
|
92
|
+
if (key === "playbackSpeed") speedRef.current = value;
|
|
93
|
+
return next;
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
[]
|
|
97
|
+
);
|
|
98
|
+
const resetSettings = useCallback(() => {
|
|
99
|
+
const defaults = { ...DEFAULTS };
|
|
100
|
+
setSettings(defaults);
|
|
101
|
+
persistSettings(defaults);
|
|
102
|
+
volumeRef.current = defaults.volume;
|
|
103
|
+
speedRef.current = defaults.playbackSpeed;
|
|
104
|
+
}, []);
|
|
105
|
+
return /* @__PURE__ */ jsx(
|
|
106
|
+
VoiceSettingsContext.Provider,
|
|
107
|
+
{
|
|
108
|
+
value: { settings, volumeRef, speedRef, updateSetting, resetSettings },
|
|
109
|
+
children
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
function useVoiceSettings() {
|
|
114
|
+
const context = useContext(VoiceSettingsContext);
|
|
115
|
+
if (context) return context;
|
|
116
|
+
return {
|
|
117
|
+
settings: DEFAULTS,
|
|
118
|
+
volumeRef: { current: DEFAULTS.volume },
|
|
119
|
+
speedRef: { current: DEFAULTS.playbackSpeed },
|
|
120
|
+
updateSetting: () => {
|
|
121
|
+
},
|
|
122
|
+
resetSettings: () => {
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/components/VoiceSettingsView.tsx
|
|
128
|
+
import { VAD, useSiteConfig } from "@unctad-ai/voice-agent-core";
|
|
129
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
130
|
+
function expressivenessLabel(v) {
|
|
131
|
+
if (v <= 0.15) return "Low";
|
|
132
|
+
if (v <= 0.4) return "Medium";
|
|
133
|
+
return "High";
|
|
134
|
+
}
|
|
135
|
+
function speechThresholdLabel(v) {
|
|
136
|
+
if (v >= 0.75) return "Strict";
|
|
137
|
+
if (v >= 0.5) return "Balanced";
|
|
138
|
+
return "Sensitive";
|
|
139
|
+
}
|
|
140
|
+
function bargeInLabel(v) {
|
|
141
|
+
if (v >= 0.8) return "Hard";
|
|
142
|
+
if (v >= 0.6) return "Normal";
|
|
143
|
+
return "Easy";
|
|
144
|
+
}
|
|
145
|
+
function SliderSetting({
|
|
146
|
+
icon,
|
|
147
|
+
label,
|
|
148
|
+
value,
|
|
149
|
+
displayValue,
|
|
150
|
+
min,
|
|
151
|
+
max,
|
|
152
|
+
step,
|
|
153
|
+
onChange
|
|
154
|
+
}) {
|
|
155
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-3 space-y-2.5", children: [
|
|
156
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
157
|
+
icon,
|
|
158
|
+
/* @__PURE__ */ jsx2("span", { className: "flex-1 text-sm font-medium text-neutral-900", children: label }),
|
|
159
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs font-medium tabular-nums", style: { color: "var(--voice-settings-accent, #DB2129)" }, children: displayValue })
|
|
160
|
+
] }),
|
|
161
|
+
/* @__PURE__ */ jsx2(
|
|
162
|
+
"input",
|
|
163
|
+
{
|
|
164
|
+
type: "range",
|
|
165
|
+
value,
|
|
166
|
+
min,
|
|
167
|
+
max,
|
|
168
|
+
step,
|
|
169
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
170
|
+
className: "w-full",
|
|
171
|
+
style: { accentColor: "var(--voice-settings-accent, #DB2129)" }
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
] });
|
|
175
|
+
}
|
|
176
|
+
function ToggleSetting({
|
|
177
|
+
icon,
|
|
178
|
+
label,
|
|
179
|
+
description,
|
|
180
|
+
checked,
|
|
181
|
+
onChange
|
|
182
|
+
}) {
|
|
183
|
+
return /* @__PURE__ */ jsxs("label", { className: "py-3 flex items-center gap-3 cursor-pointer", children: [
|
|
184
|
+
icon,
|
|
185
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
186
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-neutral-900", children: label }),
|
|
187
|
+
/* @__PURE__ */ jsx2("div", { className: "text-xs text-neutral-500", children: description })
|
|
188
|
+
] }),
|
|
189
|
+
/* @__PURE__ */ jsx2(
|
|
190
|
+
"button",
|
|
191
|
+
{
|
|
192
|
+
role: "switch",
|
|
193
|
+
"aria-checked": checked,
|
|
194
|
+
onClick: () => onChange(!checked),
|
|
195
|
+
className: "relative inline-flex h-6 w-11 items-center rounded-full transition-colors cursor-pointer",
|
|
196
|
+
style: { backgroundColor: checked ? "var(--voice-settings-accent, #DB2129)" : "#d1d5db" },
|
|
197
|
+
children: /* @__PURE__ */ jsx2(
|
|
198
|
+
"span",
|
|
199
|
+
{
|
|
200
|
+
className: "inline-block h-5 w-5 rounded-full bg-white shadow transition-transform",
|
|
201
|
+
style: { transform: checked ? "translateX(22px)" : "translateX(2px)" }
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
] });
|
|
207
|
+
}
|
|
208
|
+
function SelectSetting({
|
|
209
|
+
icon,
|
|
210
|
+
label,
|
|
211
|
+
value,
|
|
212
|
+
onChange,
|
|
213
|
+
options
|
|
214
|
+
}) {
|
|
215
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-3 flex items-center gap-3", children: [
|
|
216
|
+
icon,
|
|
217
|
+
/* @__PURE__ */ jsx2("span", { className: "flex-1 text-sm font-medium text-neutral-900", children: label }),
|
|
218
|
+
/* @__PURE__ */ jsx2(
|
|
219
|
+
"select",
|
|
220
|
+
{
|
|
221
|
+
value,
|
|
222
|
+
onChange: (e) => onChange(e.target.value),
|
|
223
|
+
className: "h-8 text-xs font-medium text-neutral-700 rounded-full border border-neutral-200 bg-neutral-50 pl-3 pr-7 outline-none appearance-none cursor-pointer hover:border-neutral-300 hover:bg-neutral-100 focus:border-neutral-400 transition-colors",
|
|
224
|
+
style: {
|
|
225
|
+
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")`,
|
|
226
|
+
backgroundRepeat: "no-repeat",
|
|
227
|
+
backgroundPosition: "right 8px center"
|
|
228
|
+
},
|
|
229
|
+
children: options.map((opt) => /* @__PURE__ */ jsx2("option", { value: opt.value, children: opt.label }, opt.value))
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
] });
|
|
233
|
+
}
|
|
234
|
+
function VoiceSettingsView({ onBack, onVolumeChange }) {
|
|
235
|
+
const { settings, updateSetting, resetSettings } = useVoiceSettings();
|
|
236
|
+
const { colors } = useSiteConfig();
|
|
237
|
+
const iconClass = "w-4 h-4 shrink-0";
|
|
238
|
+
const iconStyle = { color: colors.primary };
|
|
239
|
+
return /* @__PURE__ */ jsxs(
|
|
240
|
+
motion.div,
|
|
241
|
+
{
|
|
242
|
+
initial: { opacity: 0, x: 20 },
|
|
243
|
+
animate: { opacity: 1, x: 0 },
|
|
244
|
+
exit: { opacity: 0, x: 20 },
|
|
245
|
+
transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
|
|
246
|
+
className: "voice-settings absolute inset-0 flex flex-col z-10",
|
|
247
|
+
style: { borderRadius: "inherit", backgroundColor: "#f9fafb", "--voice-settings-accent": colors.primary },
|
|
248
|
+
children: [
|
|
249
|
+
/* @__PURE__ */ jsxs(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
className: "flex items-center gap-2.5 shrink-0 px-4",
|
|
253
|
+
style: { height: 56, borderBottom: "1px solid #e5e7eb" },
|
|
254
|
+
children: [
|
|
255
|
+
/* @__PURE__ */ jsx2(
|
|
256
|
+
"button",
|
|
257
|
+
{
|
|
258
|
+
onClick: onBack,
|
|
259
|
+
className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors",
|
|
260
|
+
style: { color: "#6b7280" },
|
|
261
|
+
onMouseEnter: (e) => {
|
|
262
|
+
e.currentTarget.style.color = "#111827";
|
|
263
|
+
},
|
|
264
|
+
onMouseLeave: (e) => {
|
|
265
|
+
e.currentTarget.style.color = "#6b7280";
|
|
266
|
+
},
|
|
267
|
+
"aria-label": "Back to conversation",
|
|
268
|
+
children: /* @__PURE__ */ jsx2(ArrowLeft, { className: "w-4 h-4" })
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
/* @__PURE__ */ jsx2("span", { className: "flex-1 text-sm font-semibold", style: { color: "#111827" }, children: "Settings" }),
|
|
272
|
+
/* @__PURE__ */ jsx2(
|
|
273
|
+
"button",
|
|
274
|
+
{
|
|
275
|
+
onClick: resetSettings,
|
|
276
|
+
className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors",
|
|
277
|
+
style: { color: "#9ca3af" },
|
|
278
|
+
onMouseEnter: (e) => {
|
|
279
|
+
e.currentTarget.style.color = colors.primary;
|
|
280
|
+
},
|
|
281
|
+
onMouseLeave: (e) => {
|
|
282
|
+
e.currentTarget.style.color = "#9ca3af";
|
|
283
|
+
},
|
|
284
|
+
"aria-label": "Reset all settings",
|
|
285
|
+
title: "Reset to defaults",
|
|
286
|
+
children: /* @__PURE__ */ jsx2(RotateCcw, { className: "w-3.5 h-3.5" })
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
),
|
|
292
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0 overflow-y-auto", children: [
|
|
293
|
+
/* @__PURE__ */ jsxs(SettingsSection, { title: "Voice Input", children: [
|
|
294
|
+
/* @__PURE__ */ jsx2(
|
|
295
|
+
ToggleSetting,
|
|
296
|
+
{
|
|
297
|
+
icon: /* @__PURE__ */ jsx2(Mic, { className: iconClass, style: iconStyle }),
|
|
298
|
+
label: "Auto-listen",
|
|
299
|
+
description: "Start mic when panel opens",
|
|
300
|
+
checked: settings.autoListen,
|
|
301
|
+
onChange: (v) => updateSetting("autoListen", v)
|
|
302
|
+
}
|
|
303
|
+
),
|
|
304
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
305
|
+
/* @__PURE__ */ jsx2(
|
|
306
|
+
SelectSetting,
|
|
307
|
+
{
|
|
308
|
+
icon: /* @__PURE__ */ jsx2(Timer, { className: iconClass, style: iconStyle }),
|
|
309
|
+
label: "Idle timeout",
|
|
310
|
+
value: String(settings.idleTimeoutMs),
|
|
311
|
+
onChange: (v) => updateSetting("idleTimeoutMs", Number(v)),
|
|
312
|
+
options: [
|
|
313
|
+
{ value: "30000", label: "30s" },
|
|
314
|
+
{ value: "60000", label: "1 min" },
|
|
315
|
+
{ value: "120000", label: "2 min" },
|
|
316
|
+
{ value: "300000", label: "5 min" }
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
),
|
|
320
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
321
|
+
/* @__PURE__ */ jsx2(
|
|
322
|
+
SliderSetting,
|
|
323
|
+
{
|
|
324
|
+
icon: /* @__PURE__ */ jsx2(Ear, { className: iconClass, style: iconStyle }),
|
|
325
|
+
label: "Speech threshold",
|
|
326
|
+
value: settings.speechThreshold * 100,
|
|
327
|
+
displayValue: speechThresholdLabel(settings.speechThreshold),
|
|
328
|
+
min: 30,
|
|
329
|
+
max: 90,
|
|
330
|
+
step: 5,
|
|
331
|
+
onChange: (v) => updateSetting("speechThreshold", v / 100)
|
|
332
|
+
}
|
|
333
|
+
),
|
|
334
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
335
|
+
/* @__PURE__ */ jsx2(
|
|
336
|
+
SelectSetting,
|
|
337
|
+
{
|
|
338
|
+
icon: /* @__PURE__ */ jsx2(Clock, { className: iconClass, style: iconStyle }),
|
|
339
|
+
label: "Pause tolerance",
|
|
340
|
+
value: String(settings.pauseToleranceMs),
|
|
341
|
+
onChange: (v) => updateSetting("pauseToleranceMs", Number(v)),
|
|
342
|
+
options: [
|
|
343
|
+
{ value: "400", label: "Fast" },
|
|
344
|
+
{ value: "600", label: "Default" },
|
|
345
|
+
{ value: "800", label: "Relaxed" },
|
|
346
|
+
{ value: "1000", label: "Patient" }
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
),
|
|
350
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
351
|
+
/* @__PURE__ */ jsx2(
|
|
352
|
+
SliderSetting,
|
|
353
|
+
{
|
|
354
|
+
icon: /* @__PURE__ */ jsx2(Zap, { className: iconClass, style: iconStyle }),
|
|
355
|
+
label: "Barge-in threshold",
|
|
356
|
+
value: settings.bargeInThreshold * 100,
|
|
357
|
+
displayValue: bargeInLabel(settings.bargeInThreshold),
|
|
358
|
+
min: 40,
|
|
359
|
+
max: 90,
|
|
360
|
+
step: 5,
|
|
361
|
+
onChange: (v) => updateSetting("bargeInThreshold", v / 100)
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
365
|
+
/* @__PURE__ */ jsx2(
|
|
366
|
+
SelectSetting,
|
|
367
|
+
{
|
|
368
|
+
icon: /* @__PURE__ */ jsx2(Minimize2, { className: iconClass, style: iconStyle }),
|
|
369
|
+
label: "Auto-collapse",
|
|
370
|
+
value: String(settings.panelCollapseTimeoutMs),
|
|
371
|
+
onChange: (v) => updateSetting("panelCollapseTimeoutMs", Number(v)),
|
|
372
|
+
options: [
|
|
373
|
+
{ value: "120000", label: "2 min" },
|
|
374
|
+
{ value: "300000", label: "5 min" },
|
|
375
|
+
{ value: "600000", label: "10 min" },
|
|
376
|
+
{ value: "0", label: "Never" }
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
] }),
|
|
381
|
+
/* @__PURE__ */ jsxs(SettingsSection, { title: "Voice Output", children: [
|
|
382
|
+
/* @__PURE__ */ jsx2(
|
|
383
|
+
ToggleSetting,
|
|
384
|
+
{
|
|
385
|
+
icon: /* @__PURE__ */ jsx2(AudioLines, { className: iconClass, style: iconStyle }),
|
|
386
|
+
label: "Text-to-speech",
|
|
387
|
+
description: "Speak responses aloud",
|
|
388
|
+
checked: settings.ttsEnabled,
|
|
389
|
+
onChange: (v) => updateSetting("ttsEnabled", v)
|
|
390
|
+
}
|
|
391
|
+
),
|
|
392
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
393
|
+
/* @__PURE__ */ jsx2(
|
|
394
|
+
SliderSetting,
|
|
395
|
+
{
|
|
396
|
+
icon: /* @__PURE__ */ jsx2(Volume2, { className: iconClass, style: iconStyle }),
|
|
397
|
+
label: "Volume",
|
|
398
|
+
value: settings.volume * 100,
|
|
399
|
+
displayValue: `${Math.round(settings.volume * 100)}%`,
|
|
400
|
+
min: 0,
|
|
401
|
+
max: 100,
|
|
402
|
+
step: 1,
|
|
403
|
+
onChange: (v) => {
|
|
404
|
+
updateSetting("volume", v / 100);
|
|
405
|
+
onVolumeChange?.(v / 100);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
),
|
|
409
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
410
|
+
/* @__PURE__ */ jsx2(
|
|
411
|
+
SliderSetting,
|
|
412
|
+
{
|
|
413
|
+
icon: /* @__PURE__ */ jsx2(Gauge, { className: iconClass, style: iconStyle }),
|
|
414
|
+
label: "Speed",
|
|
415
|
+
value: settings.playbackSpeed * 100,
|
|
416
|
+
displayValue: `${settings.playbackSpeed.toFixed(2)}x`,
|
|
417
|
+
min: 75,
|
|
418
|
+
max: 150,
|
|
419
|
+
step: 5,
|
|
420
|
+
onChange: (v) => updateSetting("playbackSpeed", v / 100)
|
|
421
|
+
}
|
|
422
|
+
),
|
|
423
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
424
|
+
/* @__PURE__ */ jsx2(
|
|
425
|
+
SliderSetting,
|
|
426
|
+
{
|
|
427
|
+
icon: /* @__PURE__ */ jsx2(Sparkles, { className: iconClass, style: iconStyle }),
|
|
428
|
+
label: "Expressiveness",
|
|
429
|
+
value: settings.expressiveness * 100,
|
|
430
|
+
displayValue: expressivenessLabel(settings.expressiveness),
|
|
431
|
+
min: 10,
|
|
432
|
+
max: 60,
|
|
433
|
+
step: 5,
|
|
434
|
+
onChange: (v) => updateSetting("expressiveness", v / 100)
|
|
435
|
+
}
|
|
436
|
+
),
|
|
437
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
438
|
+
/* @__PURE__ */ jsx2(
|
|
439
|
+
SelectSetting,
|
|
440
|
+
{
|
|
441
|
+
icon: /* @__PURE__ */ jsx2(MessageSquare, { className: iconClass, style: iconStyle }),
|
|
442
|
+
label: "Response length",
|
|
443
|
+
value: String(settings.responseLength),
|
|
444
|
+
onChange: (v) => updateSetting("responseLength", Number(v)),
|
|
445
|
+
options: [
|
|
446
|
+
{ value: "30", label: "Brief" },
|
|
447
|
+
{ value: "60", label: "Normal" },
|
|
448
|
+
{ value: "100", label: "Detailed" }
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
)
|
|
452
|
+
] }),
|
|
453
|
+
/* @__PURE__ */ jsxs(SettingsSection, { title: "Developer", children: [
|
|
454
|
+
/* @__PURE__ */ jsx2(
|
|
455
|
+
ToggleSetting,
|
|
456
|
+
{
|
|
457
|
+
icon: /* @__PURE__ */ jsx2(Activity, { className: iconClass, style: iconStyle }),
|
|
458
|
+
label: "Pipeline metrics",
|
|
459
|
+
description: "Show STT / LLM / TTS timings",
|
|
460
|
+
checked: settings.showPipelineMetrics,
|
|
461
|
+
onChange: (v) => updateSetting("showPipelineMetrics", v)
|
|
462
|
+
}
|
|
463
|
+
),
|
|
464
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
465
|
+
/* @__PURE__ */ jsx2(
|
|
466
|
+
SelectSetting,
|
|
467
|
+
{
|
|
468
|
+
icon: /* @__PURE__ */ jsx2(EyeOff, { className: iconClass, style: iconStyle }),
|
|
469
|
+
label: "Auto-hide metrics",
|
|
470
|
+
value: String(settings.pipelineMetricsAutoHideMs),
|
|
471
|
+
onChange: (v) => updateSetting("pipelineMetricsAutoHideMs", Number(v)),
|
|
472
|
+
options: [
|
|
473
|
+
{ value: "5000", label: "5s" },
|
|
474
|
+
{ value: "8000", label: "8s" },
|
|
475
|
+
{ value: "15000", label: "15s" },
|
|
476
|
+
{ value: "0", label: "Never" }
|
|
477
|
+
]
|
|
478
|
+
}
|
|
479
|
+
),
|
|
480
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
481
|
+
/* @__PURE__ */ jsx2(
|
|
482
|
+
SelectSetting,
|
|
483
|
+
{
|
|
484
|
+
icon: /* @__PURE__ */ jsx2(Mic, { className: iconClass, style: iconStyle }),
|
|
485
|
+
label: "STT timeout",
|
|
486
|
+
value: String(settings.sttTimeoutMs),
|
|
487
|
+
onChange: (v) => updateSetting("sttTimeoutMs", Number(v)),
|
|
488
|
+
options: [
|
|
489
|
+
{ value: "10000", label: "10s" },
|
|
490
|
+
{ value: "15000", label: "15s" },
|
|
491
|
+
{ value: "30000", label: "30s" }
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
),
|
|
495
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
496
|
+
/* @__PURE__ */ jsx2(
|
|
497
|
+
SelectSetting,
|
|
498
|
+
{
|
|
499
|
+
icon: /* @__PURE__ */ jsx2(AudioLines, { className: iconClass, style: iconStyle }),
|
|
500
|
+
label: "TTS timeout",
|
|
501
|
+
value: String(settings.ttsTimeoutMs),
|
|
502
|
+
onChange: (v) => updateSetting("ttsTimeoutMs", Number(v)),
|
|
503
|
+
options: [
|
|
504
|
+
{ value: "30000", label: "30s" },
|
|
505
|
+
{ value: "55000", label: "55s" },
|
|
506
|
+
{ value: "90000", label: "90s" }
|
|
507
|
+
]
|
|
508
|
+
}
|
|
509
|
+
),
|
|
510
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
511
|
+
/* @__PURE__ */ jsx2(
|
|
512
|
+
SelectSetting,
|
|
513
|
+
{
|
|
514
|
+
icon: /* @__PURE__ */ jsx2(Sparkles, { className: iconClass, style: iconStyle }),
|
|
515
|
+
label: "LLM timeout",
|
|
516
|
+
value: String(settings.llmTimeoutMs),
|
|
517
|
+
onChange: (v) => updateSetting("llmTimeoutMs", Number(v)),
|
|
518
|
+
options: [
|
|
519
|
+
{ value: "10000", label: "10s" },
|
|
520
|
+
{ value: "20000", label: "20s" },
|
|
521
|
+
{ value: "30000", label: "30s" },
|
|
522
|
+
{ value: "60000", label: "60s" }
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
),
|
|
526
|
+
/* @__PURE__ */ jsx2(Divider, {}),
|
|
527
|
+
/* @__PURE__ */ jsx2(
|
|
528
|
+
SelectSetting,
|
|
529
|
+
{
|
|
530
|
+
icon: /* @__PURE__ */ jsx2(Signal, { className: iconClass, style: iconStyle }),
|
|
531
|
+
label: "Min audio level",
|
|
532
|
+
value: String(settings.minAudioRms),
|
|
533
|
+
onChange: (v) => updateSetting("minAudioRms", Number(v)),
|
|
534
|
+
options: [
|
|
535
|
+
{ value: "0.01", label: "Sensitive" },
|
|
536
|
+
{ value: "0.02", label: "Default" },
|
|
537
|
+
{ value: "0.035", label: "Moderate" },
|
|
538
|
+
{ value: "0.05", label: "Strict" }
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
)
|
|
542
|
+
] }),
|
|
543
|
+
/* @__PURE__ */ jsx2(SettingsSection, { title: "Info", last: true, children: /* @__PURE__ */ jsxs("div", { className: "py-3 flex items-start gap-3", children: [
|
|
544
|
+
/* @__PURE__ */ jsx2(Info, { className: iconClass, style: { ...iconStyle, marginTop: 2 } }),
|
|
545
|
+
/* @__PURE__ */ jsx2("div", { className: "space-y-1 text-xs text-neutral-500", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
546
|
+
"VAD Threshold:",
|
|
547
|
+
" ",
|
|
548
|
+
/* @__PURE__ */ jsx2("span", { className: "font-medium text-neutral-700", children: VAD.positiveSpeechThreshold })
|
|
549
|
+
] }) })
|
|
550
|
+
] }) })
|
|
551
|
+
] })
|
|
552
|
+
]
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
function SettingsSection({
|
|
557
|
+
title,
|
|
558
|
+
children,
|
|
559
|
+
last
|
|
560
|
+
}) {
|
|
561
|
+
return /* @__PURE__ */ jsxs("div", { style: { borderBottom: last ? "none" : "1px solid #e5e7eb" }, children: [
|
|
562
|
+
/* @__PURE__ */ jsx2(
|
|
563
|
+
"div",
|
|
564
|
+
{
|
|
565
|
+
className: "px-4 pt-4 pb-1 text-[11px] font-semibold uppercase tracking-wider",
|
|
566
|
+
style: { color: "#9ca3af" },
|
|
567
|
+
children: title
|
|
568
|
+
}
|
|
569
|
+
),
|
|
570
|
+
/* @__PURE__ */ jsx2("div", { className: "px-4", children })
|
|
571
|
+
] });
|
|
572
|
+
}
|
|
573
|
+
function Divider() {
|
|
574
|
+
return /* @__PURE__ */ jsx2("div", { style: { height: 1, backgroundColor: "#f3f4f6" } });
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
export {
|
|
578
|
+
VoiceSettingsProvider,
|
|
579
|
+
useVoiceSettings,
|
|
580
|
+
VoiceSettingsView
|
|
581
|
+
};
|
|
582
|
+
//# sourceMappingURL=chunk-MUFUZ3TH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/VoiceSettingsView.tsx","../src/contexts/VoiceSettingsContext.tsx"],"sourcesContent":["import { motion } 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 EyeOff,\n Info,\n Ear,\n Clock,\n Zap,\n Minimize2,\n Signal,\n} from 'lucide-react';\nimport { useVoiceSettings } from '../contexts/VoiceSettingsContext';\nimport { VAD, useSiteConfig } from '@unctad-ai/voice-agent-core';\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\n/** Slider setting row */\nfunction 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 return (\n <div className=\"py-3 space-y-2.5\">\n <div className=\"flex items-center gap-3\">\n {icon}\n <span className=\"flex-1 text-sm font-medium text-neutral-900\">{label}</span>\n <span className=\"text-xs font-medium tabular-nums\" style={{ color: 'var(--voice-settings-accent, #DB2129)' }}>{displayValue}</span>\n </div>\n <input\n type=\"range\"\n value={value}\n min={min}\n max={max}\n step={step}\n onChange={(e) => onChange(Number(e.target.value))}\n className=\"w-full\"\n style={{ accentColor: 'var(--voice-settings-accent, #DB2129)' }}\n />\n </div>\n );\n}\n\n/** Toggle setting row */\nfunction 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 className=\"py-3 flex items-center gap-3 cursor-pointer\">\n {icon}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-neutral-900\">{label}</div>\n <div className=\"text-xs text-neutral-500\">{description}</div>\n </div>\n <button\n role=\"switch\"\n aria-checked={checked}\n onClick={() => onChange(!checked)}\n className=\"relative inline-flex h-6 w-11 items-center rounded-full transition-colors cursor-pointer\"\n style={{ backgroundColor: checked ? 'var(--voice-settings-accent, #DB2129)' : '#d1d5db' }}\n >\n <span\n className=\"inline-block h-5 w-5 rounded-full bg-white shadow transition-transform\"\n style={{ transform: checked ? 'translateX(22px)' : 'translateX(2px)' }}\n />\n </button>\n </label>\n );\n}\n\n/** Select setting row */\nfunction 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 className=\"py-3 flex items-center gap-3\">\n {icon}\n <span className=\"flex-1 text-sm font-medium text-neutral-900\">{label}</span>\n <select\n value={value}\n onChange={(e) => onChange(e.target.value)}\n className=\"h-8 text-xs font-medium text-neutral-700 rounded-full border border-neutral-200 bg-neutral-50 pl-3 pr-7 outline-none appearance-none cursor-pointer hover:border-neutral-300 hover:bg-neutral-100 focus:border-neutral-400 transition-colors\"\n style={{\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 { colors } = useSiteConfig();\n const iconClass = 'w-4 h-4 shrink-0';\n const iconStyle = { color: colors.primary };\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 className=\"voice-settings absolute inset-0 flex flex-col z-10\"\n style={{ borderRadius: 'inherit', backgroundColor: '#f9fafb', '--voice-settings-accent': colors.primary } as React.CSSProperties}\n >\n {/* Header */}\n <div\n className=\"flex items-center gap-2.5 shrink-0 px-4\"\n style={{ height: 56, borderBottom: '1px solid #e5e7eb' }}\n >\n <button\n onClick={onBack}\n className=\"w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors\"\n style={{ color: '#6b7280' }}\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 className=\"w-4 h-4\" />\n </button>\n <span className=\"flex-1 text-sm font-semibold\" style={{ color: '#111827' }}>\n Settings\n </span>\n <button\n onClick={resetSettings}\n className=\"w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors\"\n style={{ color: '#9ca3af' }}\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 className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n\n {/* Scrollable content */}\n <div className=\"flex-1 min-h-0 overflow-y-auto\">\n {/* Voice Input */}\n <SettingsSection title=\"Voice Input\">\n <ToggleSetting\n icon={<Mic className={iconClass} 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 <SelectSetting\n icon={<Timer className={iconClass} 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 <SliderSetting\n icon={<Ear className={iconClass} 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 className={iconClass} 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 className={iconClass} 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 <Divider />\n <SelectSetting\n icon={<Minimize2 className={iconClass} 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 {/* Voice Output */}\n <SettingsSection title=\"Voice Output\">\n <ToggleSetting\n icon={<AudioLines className={iconClass} 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 className={iconClass} 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 className={iconClass} 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 className={iconClass} 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 <Divider />\n <SelectSetting\n icon={<MessageSquare className={iconClass} 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 </SettingsSection>\n\n {/* Developer */}\n <SettingsSection title=\"Developer\">\n <ToggleSetting\n icon={<Activity className={iconClass} 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 className={iconClass} 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 className={iconClass} 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 className={iconClass} 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={<Sparkles className={iconClass} 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 className={iconClass} 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 </SettingsSection>\n\n {/* Info */}\n <SettingsSection title=\"Info\" last>\n <div className=\"py-3 flex items-start gap-3\">\n <Info className={iconClass} style={{ ...iconStyle, marginTop: 2 }} />\n <div className=\"space-y-1 text-xs text-neutral-500\">\n <div>\n VAD Threshold:{' '}\n <span className=\"font-medium text-neutral-700\">{VAD.positiveSpeechThreshold}</span>\n </div>\n </div>\n </div>\n </SettingsSection>\n </div>\n </motion.div>\n );\n}\n\nfunction SettingsSection({\n title,\n children,\n last,\n}: {\n title: string;\n children: React.ReactNode;\n last?: boolean;\n}) {\n return (\n <div style={{ borderBottom: last ? 'none' : '1px solid #e5e7eb' }}>\n <div\n className=\"px-4 pt-4 pb-1 text-[11px] font-semibold uppercase tracking-wider\"\n style={{ color: '#9ca3af' }}\n >\n {title}\n </div>\n <div className=\"px-4\">{children}</div>\n </div>\n );\n}\n\nfunction 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} 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}\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};\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\nexport function VoiceSettingsProvider({ children }: { children: ReactNode }) {\n const [settings, setSettings] = useState<VoiceSettings>(loadSettings);\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\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 };\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"],"mappings":"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","names":["jsx"]}
|