@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.
Files changed (85) hide show
  1. package/dist/VoiceSettingsView-RP5CUCKI.js +7 -0
  2. package/dist/VoiceSettingsView-RP5CUCKI.js.map +1 -0
  3. package/dist/chunk-MUFUZ3TH.js +582 -0
  4. package/dist/chunk-MUFUZ3TH.js.map +1 -0
  5. package/dist/index.d.ts +184 -22
  6. package/dist/index.js +3287 -21
  7. package/dist/index.js.map +1 -1
  8. package/package.json +6 -5
  9. package/dist/VoiceAgentProvider.d.ts +0 -6
  10. package/dist/VoiceAgentProvider.d.ts.map +0 -1
  11. package/dist/VoiceAgentProvider.js +0 -8
  12. package/dist/VoiceAgentProvider.js.map +0 -1
  13. package/dist/components/AgentAvatar.d.ts +0 -16
  14. package/dist/components/AgentAvatar.d.ts.map +0 -1
  15. package/dist/components/AgentAvatar.js +0 -186
  16. package/dist/components/AgentAvatar.js.map +0 -1
  17. package/dist/components/GlassCopilotPanel.d.ts +0 -13
  18. package/dist/components/GlassCopilotPanel.d.ts.map +0 -1
  19. package/dist/components/GlassCopilotPanel.js +0 -438
  20. package/dist/components/GlassCopilotPanel.js.map +0 -1
  21. package/dist/components/PipelineMetricsBar.d.ts +0 -7
  22. package/dist/components/PipelineMetricsBar.d.ts.map +0 -1
  23. package/dist/components/PipelineMetricsBar.js +0 -62
  24. package/dist/components/PipelineMetricsBar.js.map +0 -1
  25. package/dist/components/VoiceA11yAnnouncer.d.ts +0 -8
  26. package/dist/components/VoiceA11yAnnouncer.d.ts.map +0 -1
  27. package/dist/components/VoiceA11yAnnouncer.js +0 -32
  28. package/dist/components/VoiceA11yAnnouncer.js.map +0 -1
  29. package/dist/components/VoiceControls.d.ts +0 -9
  30. package/dist/components/VoiceControls.d.ts.map +0 -1
  31. package/dist/components/VoiceControls.js +0 -60
  32. package/dist/components/VoiceControls.js.map +0 -1
  33. package/dist/components/VoiceCopilotFAB.d.ts +0 -9
  34. package/dist/components/VoiceCopilotFAB.d.ts.map +0 -1
  35. package/dist/components/VoiceCopilotFAB.js +0 -12
  36. package/dist/components/VoiceCopilotFAB.js.map +0 -1
  37. package/dist/components/VoiceDotRing.d.ts +0 -9
  38. package/dist/components/VoiceDotRing.d.ts.map +0 -1
  39. package/dist/components/VoiceDotRing.js +0 -64
  40. package/dist/components/VoiceDotRing.js.map +0 -1
  41. package/dist/components/VoiceErrorBoundary.d.ts +0 -19
  42. package/dist/components/VoiceErrorBoundary.d.ts.map +0 -1
  43. package/dist/components/VoiceErrorBoundary.js +0 -24
  44. package/dist/components/VoiceErrorBoundary.js.map +0 -1
  45. package/dist/components/VoiceErrorDisplay.d.ts +0 -8
  46. package/dist/components/VoiceErrorDisplay.d.ts.map +0 -1
  47. package/dist/components/VoiceErrorDisplay.js +0 -94
  48. package/dist/components/VoiceErrorDisplay.js.map +0 -1
  49. package/dist/components/VoiceOnboarding.d.ts +0 -8
  50. package/dist/components/VoiceOnboarding.d.ts.map +0 -1
  51. package/dist/components/VoiceOnboarding.js +0 -28
  52. package/dist/components/VoiceOnboarding.js.map +0 -1
  53. package/dist/components/VoiceOrb.d.ts +0 -10
  54. package/dist/components/VoiceOrb.d.ts.map +0 -1
  55. package/dist/components/VoiceOrb.js +0 -236
  56. package/dist/components/VoiceOrb.js.map +0 -1
  57. package/dist/components/VoiceOverlay.d.ts +0 -9
  58. package/dist/components/VoiceOverlay.d.ts.map +0 -1
  59. package/dist/components/VoiceOverlay.js +0 -199
  60. package/dist/components/VoiceOverlay.js.map +0 -1
  61. package/dist/components/VoiceSettingsView.d.ts +0 -7
  62. package/dist/components/VoiceSettingsView.d.ts.map +0 -1
  63. package/dist/components/VoiceSettingsView.js +0 -109
  64. package/dist/components/VoiceSettingsView.js.map +0 -1
  65. package/dist/components/VoiceToolCard.d.ts +0 -11
  66. package/dist/components/VoiceToolCard.d.ts.map +0 -1
  67. package/dist/components/VoiceToolCard.js +0 -37
  68. package/dist/components/VoiceToolCard.js.map +0 -1
  69. package/dist/components/VoiceTranscript.d.ts +0 -13
  70. package/dist/components/VoiceTranscript.d.ts.map +0 -1
  71. package/dist/components/VoiceTranscript.js +0 -313
  72. package/dist/components/VoiceTranscript.js.map +0 -1
  73. package/dist/components/VoiceWaveformCanvas.d.ts +0 -9
  74. package/dist/components/VoiceWaveformCanvas.d.ts.map +0 -1
  75. package/dist/components/VoiceWaveformCanvas.js +0 -91
  76. package/dist/components/VoiceWaveformCanvas.js.map +0 -1
  77. package/dist/contexts/VoiceSettingsContext.d.ts +0 -37
  78. package/dist/contexts/VoiceSettingsContext.d.ts.map +0 -1
  79. package/dist/contexts/VoiceSettingsContext.js +0 -89
  80. package/dist/contexts/VoiceSettingsContext.js.map +0 -1
  81. package/dist/index.d.ts.map +0 -1
  82. package/dist/utils.d.ts +0 -3
  83. package/dist/utils.d.ts.map +0 -1
  84. package/dist/utils.js +0 -6
  85. package/dist/utils.js.map +0 -1
@@ -0,0 +1,7 @@
1
+ import {
2
+ VoiceSettingsView
3
+ } from "./chunk-MUFUZ3TH.js";
4
+ export {
5
+ VoiceSettingsView as default
6
+ };
7
+ //# sourceMappingURL=VoiceSettingsView-RP5CUCKI.js.map
@@ -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"]}