@unctad-ai/voice-agent-ui 5.0.5 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import {
5
5
  SliderSetting,
6
6
  ToggleSetting,
7
7
  VoiceSettingsView
8
- } from "./chunk-UCP5K6TJ.js";
8
+ } from "./chunk-JUEDGAZN.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-OHBSNGY7.js.map
17
+ //# sourceMappingURL=VoiceSettingsView-ZONLN45Z.js.map
@@ -156,8 +156,13 @@ import { VAD, useSiteConfig as useSiteConfig2, usePersonaContext as usePersonaCo
156
156
 
157
157
  // src/components/PersonaSettings.tsx
158
158
  import { useState as useState2, useRef as useRef2, useEffect, useCallback as useCallback2 } from "react";
159
- import { usePersonaContext, useSiteConfig } from "@unctad-ai/voice-agent-core";
159
+ import { usePersonaContext, useSiteConfig, useVoiceRecorder } from "@unctad-ai/voice-agent-core";
160
160
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
161
+ var RECORDING_PROMPTS = [
162
+ "Good morning. I am here to help you with your registration process today.",
163
+ "Could you please provide your business name and the type of license you need?",
164
+ "Thank you for your patience. Your application has been submitted successfully."
165
+ ];
161
166
  var LANGUAGE_OPTIONS = [
162
167
  { value: "en", label: "English" },
163
168
  { value: "fr", label: "French" },
@@ -177,6 +182,7 @@ function PersonaSettings({ adminPassword }) {
177
182
  function PersonaSettingsInner({ adminPassword }) {
178
183
  const config = useSiteConfig();
179
184
  const persona = usePersonaContext();
185
+ const [showRecording, setShowRecording] = useState2(false);
180
186
  if (!persona) return null;
181
187
  const { persona: data, isLoaded, uploadAvatar, uploadVoice, deleteVoice, setActiveVoice, previewVoice, updateConfig } = persona;
182
188
  const isAdmin = adminPassword !== null;
@@ -191,6 +197,23 @@ function PersonaSettingsInner({ adminPassword }) {
191
197
  if (!isLoaded) {
192
198
  return /* @__PURE__ */ jsx2("div", { style: { padding: 16, fontSize: 13, color: "#9ca3af", fontFamily: "inherit" }, children: "Loading persona settings..." });
193
199
  }
200
+ if (showRecording) {
201
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12, fontFamily: "inherit" }, children: [
202
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Record voice sample" }),
203
+ /* @__PURE__ */ jsx2(
204
+ RecordingFlow,
205
+ {
206
+ onComplete: async (blob, name) => {
207
+ const file = new File([blob], `${name}.wav`, { type: "audio/wav" });
208
+ await uploadVoice(file, name, adminPassword ?? void 0);
209
+ setShowRecording(false);
210
+ },
211
+ onCancel: () => setShowRecording(false),
212
+ primaryColor: config.colors.primary
213
+ }
214
+ )
215
+ ] });
216
+ }
194
217
  return /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexDirection: "column", gap: 12, fontFamily: "inherit" }, children: isAdmin ? /* @__PURE__ */ jsxs(Fragment, { children: [
195
218
  /* @__PURE__ */ jsx2(AvatarSection, { avatarUrl: config.avatarUrl, name: config.copilotName, onUpload: (f) => uploadAvatar(f, adminPassword) }),
196
219
  /* @__PURE__ */ jsx2(NameSection, { name: config.copilotName, onSave: (n) => updateConfig({ copilotName: n }, adminPassword), primaryColor: config.colors.primary }),
@@ -203,7 +226,8 @@ function PersonaSettingsInner({ adminPassword }) {
203
226
  onDelete: (id) => deleteVoice(id, adminPassword),
204
227
  onSelect: (id) => setActiveVoice(id, adminPassword),
205
228
  onPreview: previewVoice,
206
- primaryColor: config.colors.primary
229
+ primaryColor: config.colors.primary,
230
+ onRecord: () => setShowRecording(true)
207
231
  }
208
232
  ),
209
233
  /* @__PURE__ */ jsxs("div", { style: { borderTop: "1px solid #e5e7eb", paddingTop: 12, display: "flex", flexDirection: "column", gap: 10 }, children: [
@@ -532,7 +556,7 @@ function NameSection({ name, onSave, primaryColor }) {
532
556
  ] })
533
557
  ] });
534
558
  }
535
- function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onPreview, primaryColor, disabled }) {
559
+ function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onPreview, primaryColor, disabled, onRecord }) {
536
560
  const [uploading, setUploading] = useState2(false);
537
561
  const [uploadName, setUploadName] = useState2("");
538
562
  const [showUpload, setShowUpload] = useState2(false);
@@ -579,7 +603,7 @@ function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onP
579
603
  };
580
604
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, paddingTop: 4, paddingBottom: 4 }, children: [
581
605
  /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Voice" }),
582
- voices.length === 0 && /* @__PURE__ */ jsx2("p", { style: { fontSize: 11, color: "#9ca3af", margin: 0 }, children: "No voices configured. Upload a WAV sample to enable voice cloning." }),
606
+ voices.length === 0 && /* @__PURE__ */ jsx2("p", { style: { fontSize: 11, color: "#9ca3af", margin: 0 }, children: "No voices configured. Record or upload a WAV sample to enable voice cloning." }),
583
607
  voices.map((v) => /* @__PURE__ */ jsx2(
584
608
  VoiceRow,
585
609
  {
@@ -607,13 +631,24 @@ function VoiceSection({ voices, activeVoiceId, onUpload, onDelete, onSelect, onP
607
631
  },
608
632
  primaryColor
609
633
  }
610
- ) : /* @__PURE__ */ jsx2(
611
- UploadButton,
612
- {
613
- disabled: voices.length >= 10,
614
- onClick: () => inputRef.current?.click()
615
- }
616
- )),
634
+ ) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
635
+ onRecord && /* @__PURE__ */ jsx2(
636
+ DashedButton,
637
+ {
638
+ disabled: voices.length >= 10,
639
+ onClick: onRecord,
640
+ label: "Record voice sample"
641
+ }
642
+ ),
643
+ /* @__PURE__ */ jsx2(
644
+ DashedButton,
645
+ {
646
+ disabled: voices.length >= 10,
647
+ onClick: () => inputRef.current?.click(),
648
+ label: "Upload WAV (max 45s)"
649
+ }
650
+ )
651
+ ] })),
617
652
  !disabled && /* @__PURE__ */ jsx2("input", { ref: inputRef, type: "file", accept: "audio/wav", onChange: handleFileSelect, style: { display: "none" } })
618
653
  ] });
619
654
  }
@@ -768,7 +803,7 @@ function UploadForm({ uploadName, uploading, onNameChange, onUpload, onCancel, p
768
803
  ] })
769
804
  ] });
770
805
  }
771
- function UploadButton({ disabled, onClick }) {
806
+ function DashedButton({ disabled, onClick, label }) {
772
807
  const [hovered, setHovered] = useState2(false);
773
808
  return /* @__PURE__ */ jsx2(
774
809
  "button",
@@ -778,11 +813,9 @@ function UploadButton({ disabled, onClick }) {
778
813
  onMouseEnter: () => setHovered(true),
779
814
  onMouseLeave: () => setHovered(false),
780
815
  style: {
816
+ flex: 1,
781
817
  fontSize: 11,
782
- paddingLeft: 10,
783
- paddingRight: 10,
784
- paddingTop: 6,
785
- paddingBottom: 6,
818
+ padding: "6px 10px",
786
819
  borderRadius: 8,
787
820
  border: "1px dashed #d1d5db",
788
821
  backgroundColor: hovered && !disabled ? "#f9fafb" : "#fff",
@@ -791,10 +824,374 @@ function UploadButton({ disabled, onClick }) {
791
824
  transition: "background-color 0.15s",
792
825
  fontFamily: "inherit"
793
826
  },
794
- children: "+ Upload voice sample (WAV, max 30s)"
827
+ children: label
795
828
  }
796
829
  );
797
830
  }
831
+ function RecordingFlow({ onComplete, onCancel, primaryColor }) {
832
+ const recorder = useVoiceRecorder();
833
+ const [name, setName] = useState2("Recording");
834
+ const [uploading, setUploading] = useState2(false);
835
+ useEffect(() => {
836
+ recorder.prepare();
837
+ }, []);
838
+ const handleUpload = async () => {
839
+ if (!recorder.wavBlob) return;
840
+ setUploading(true);
841
+ try {
842
+ await onComplete(recorder.wavBlob, name);
843
+ } catch (err) {
844
+ console.error("Voice upload failed:", err);
845
+ } finally {
846
+ setUploading(false);
847
+ }
848
+ };
849
+ if (recorder.loading) {
850
+ return /* @__PURE__ */ jsx2("div", { style: { padding: 12, fontSize: 12, color: "#6b7280", textAlign: "center", fontFamily: "inherit" }, children: "Preparing microphone..." });
851
+ }
852
+ if (recorder.error) {
853
+ return /* @__PURE__ */ jsxs("div", { style: {
854
+ padding: 12,
855
+ borderRadius: 8,
856
+ border: "1px solid #fca5a5",
857
+ backgroundColor: "#fef2f2",
858
+ fontSize: 12,
859
+ fontFamily: "inherit"
860
+ }, children: [
861
+ /* @__PURE__ */ jsx2("div", { style: { color: "#dc2626", marginBottom: 8 }, children: "Could not access microphone. Please check your browser permissions." }),
862
+ /* @__PURE__ */ jsx2("button", { onClick: onCancel, style: {
863
+ fontSize: 11,
864
+ padding: "4px 10px",
865
+ borderRadius: 6,
866
+ border: "1px solid #e5e7eb",
867
+ backgroundColor: "#fff",
868
+ cursor: "pointer",
869
+ fontFamily: "inherit"
870
+ }, children: "Cancel" })
871
+ ] });
872
+ }
873
+ if (recorder.state === "ready") {
874
+ return /* @__PURE__ */ jsxs("div", { style: {
875
+ padding: 12,
876
+ borderRadius: 10,
877
+ border: "1px solid #e5e7eb",
878
+ backgroundColor: "#f9fafb",
879
+ display: "flex",
880
+ flexDirection: "column",
881
+ gap: 10,
882
+ fontFamily: "inherit"
883
+ }, children: [
884
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 11, color: "#6b7280" }, children: "Use a quiet room and speak naturally." }),
885
+ /* @__PURE__ */ jsx2(PromptSentences, {}),
886
+ /* @__PURE__ */ jsx2(LevelMeter, { rms: recorder.rmsLevel, active: false }),
887
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
888
+ /* @__PURE__ */ jsxs("button", { onClick: recorder.startRecording, style: {
889
+ flex: 1,
890
+ padding: 8,
891
+ borderRadius: 8,
892
+ border: "none",
893
+ backgroundColor: "#dc2626",
894
+ color: "#fff",
895
+ fontSize: 12,
896
+ fontWeight: 500,
897
+ cursor: "pointer",
898
+ fontFamily: "inherit",
899
+ display: "flex",
900
+ alignItems: "center",
901
+ justifyContent: "center",
902
+ gap: 6
903
+ }, children: [
904
+ /* @__PURE__ */ jsx2("span", { style: {
905
+ width: 8,
906
+ height: 8,
907
+ borderRadius: "50%",
908
+ background: "#fff",
909
+ display: "inline-block"
910
+ } }),
911
+ "Start recording"
912
+ ] }),
913
+ /* @__PURE__ */ jsx2("button", { onClick: onCancel, style: {
914
+ padding: "8px 12px",
915
+ borderRadius: 8,
916
+ border: "1px solid #e5e7eb",
917
+ backgroundColor: "#fff",
918
+ fontSize: 12,
919
+ color: "#6b7280",
920
+ cursor: "pointer",
921
+ fontFamily: "inherit"
922
+ }, children: "Cancel" })
923
+ ] })
924
+ ] });
925
+ }
926
+ if (recorder.state === "recording") {
927
+ return /* @__PURE__ */ jsxs("div", { style: {
928
+ padding: 12,
929
+ borderRadius: 10,
930
+ border: "1px solid #fca5a5",
931
+ backgroundColor: "#fef2f2",
932
+ display: "flex",
933
+ flexDirection: "column",
934
+ gap: 10,
935
+ fontFamily: "inherit"
936
+ }, children: [
937
+ /* @__PURE__ */ jsx2(PromptSentences, {}),
938
+ /* @__PURE__ */ jsx2(LevelMeter, { rms: recorder.rmsLevel, active: true, elapsed: recorder.elapsed, maxDuration: 45 }),
939
+ /* @__PURE__ */ jsxs("button", { onClick: recorder.stop, style: {
940
+ width: "100%",
941
+ padding: 8,
942
+ borderRadius: 8,
943
+ border: "none",
944
+ backgroundColor: "#1f2937",
945
+ color: "#fff",
946
+ fontSize: 12,
947
+ fontWeight: 500,
948
+ cursor: "pointer",
949
+ fontFamily: "inherit",
950
+ display: "flex",
951
+ alignItems: "center",
952
+ justifyContent: "center",
953
+ gap: 6
954
+ }, children: [
955
+ /* @__PURE__ */ jsx2("span", { style: {
956
+ width: 8,
957
+ height: 8,
958
+ borderRadius: 2,
959
+ background: "#fff",
960
+ display: "inline-block"
961
+ } }),
962
+ "Stop recording"
963
+ ] })
964
+ ] });
965
+ }
966
+ return /* @__PURE__ */ jsxs("div", { style: {
967
+ padding: 12,
968
+ borderRadius: 10,
969
+ border: "1px solid #e5e7eb",
970
+ backgroundColor: "#f9fafb",
971
+ display: "flex",
972
+ flexDirection: "column",
973
+ gap: 10,
974
+ fontFamily: "inherit"
975
+ }, children: [
976
+ /* @__PURE__ */ jsx2(WaveformPreview, { blob: recorder.wavBlob, duration: recorder.elapsed }),
977
+ /* @__PURE__ */ jsx2(QualityBadge, { warning: recorder.qualityWarning }),
978
+ /* @__PURE__ */ jsx2(
979
+ "input",
980
+ {
981
+ type: "text",
982
+ value: name,
983
+ onChange: (e) => setName(e.target.value),
984
+ placeholder: "Voice name",
985
+ style: {
986
+ width: "100%",
987
+ fontSize: 13,
988
+ padding: "6px 10px",
989
+ borderRadius: 8,
990
+ border: "1px solid #e5e7eb",
991
+ outline: "none",
992
+ fontFamily: "inherit",
993
+ boxSizing: "border-box"
994
+ }
995
+ }
996
+ ),
997
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
998
+ !recorder.qualityWarning?.blocking && /* @__PURE__ */ jsx2("button", { onClick: handleUpload, disabled: uploading || !name, style: {
999
+ flex: 1,
1000
+ padding: 8,
1001
+ borderRadius: 8,
1002
+ border: "none",
1003
+ backgroundColor: "#1f2937",
1004
+ color: "#fff",
1005
+ fontSize: 12,
1006
+ fontWeight: 500,
1007
+ cursor: uploading || !name ? "default" : "pointer",
1008
+ opacity: uploading || !name ? 0.5 : 1,
1009
+ fontFamily: "inherit"
1010
+ }, children: uploading ? "Processing (~8s)..." : "Use this recording" }),
1011
+ /* @__PURE__ */ jsx2("button", { onClick: recorder.reset, style: {
1012
+ padding: "8px 12px",
1013
+ borderRadius: 8,
1014
+ border: "1px solid #e5e7eb",
1015
+ backgroundColor: "#fff",
1016
+ fontSize: 12,
1017
+ color: "#6b7280",
1018
+ cursor: "pointer",
1019
+ fontFamily: "inherit"
1020
+ }, children: "Re-record" })
1021
+ ] })
1022
+ ] });
1023
+ }
1024
+ function PromptSentences() {
1025
+ return /* @__PURE__ */ jsxs("div", { style: {
1026
+ background: "#fff",
1027
+ border: "1px solid #e5e7eb",
1028
+ borderRadius: 8,
1029
+ padding: 12
1030
+ }, children: [
1031
+ /* @__PURE__ */ jsx2("div", { style: {
1032
+ fontSize: 10,
1033
+ color: "#9ca3af",
1034
+ textTransform: "uppercase",
1035
+ letterSpacing: "0.05em",
1036
+ marginBottom: 8
1037
+ }, children: "Read aloud" }),
1038
+ RECORDING_PROMPTS.map((sentence, i) => /* @__PURE__ */ jsxs("div", { style: {
1039
+ fontSize: 13,
1040
+ color: "#374151",
1041
+ lineHeight: 1.6,
1042
+ marginBottom: i < RECORDING_PROMPTS.length - 1 ? 6 : 0
1043
+ }, children: [
1044
+ i + 1,
1045
+ ". \u201C",
1046
+ sentence,
1047
+ "\u201D"
1048
+ ] }, i)),
1049
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 10, color: "#9ca3af", marginTop: 8, fontStyle: "italic" }, children: "Pause briefly between each sentence." })
1050
+ ] });
1051
+ }
1052
+ function LevelMeter({ rms, active, elapsed, maxDuration }) {
1053
+ const color = active ? "#dc2626" : "#d1d5db";
1054
+ const barCount = 8;
1055
+ const heights = Array.from({ length: barCount }, (_, i) => {
1056
+ const base = Math.min(1, rms * 5);
1057
+ const variation = Math.sin(i * 1.7 + (elapsed ?? 0) * 3) * 0.3 + 0.7;
1058
+ return 4 + base * variation * 16;
1059
+ });
1060
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1061
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 2, alignItems: "end", height: 20 }, children: heights.map((h, i) => /* @__PURE__ */ jsx2("div", { style: {
1062
+ width: 3,
1063
+ height: h,
1064
+ backgroundColor: color,
1065
+ borderRadius: 1,
1066
+ transition: "height 0.1s"
1067
+ } }, i)) }),
1068
+ active && elapsed != null && maxDuration != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
1069
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: "#dc2626", fontWeight: 500 }, children: [
1070
+ formatTime(elapsed),
1071
+ " / ",
1072
+ formatTime(maxDuration)
1073
+ ] }),
1074
+ /* @__PURE__ */ jsx2("span", { style: {
1075
+ width: 6,
1076
+ height: 6,
1077
+ borderRadius: "50%",
1078
+ backgroundColor: "#dc2626",
1079
+ display: "inline-block"
1080
+ } })
1081
+ ] }) : /* @__PURE__ */ jsx2("span", { style: { fontSize: 11, color: "#9ca3af" }, children: "Mic ready" })
1082
+ ] });
1083
+ }
1084
+ function formatTime(sec) {
1085
+ const m = Math.floor(sec / 60);
1086
+ const s = Math.floor(sec % 60);
1087
+ return `${m}:${s.toString().padStart(2, "0")}`;
1088
+ }
1089
+ function WaveformPreview({ blob, duration }) {
1090
+ const [playing, setPlaying] = useState2(false);
1091
+ const [bars, setBars] = useState2([]);
1092
+ const audioRef = useRef2(null);
1093
+ const barCount = 60;
1094
+ useEffect(() => {
1095
+ if (!blob) return;
1096
+ blob.arrayBuffer().then((buf) => {
1097
+ const samples = new Int16Array(buf, 44);
1098
+ const chunkSize = Math.max(1, Math.floor(samples.length / barCount));
1099
+ const result = [];
1100
+ for (let i = 0; i < barCount; i++) {
1101
+ let maxAbs = 0;
1102
+ const start = i * chunkSize;
1103
+ const end = Math.min(start + chunkSize, samples.length);
1104
+ for (let j = start; j < end; j++) {
1105
+ const abs = Math.abs(samples[j]) / 32768;
1106
+ if (abs > maxAbs) maxAbs = abs;
1107
+ }
1108
+ result.push(maxAbs);
1109
+ }
1110
+ setBars(result);
1111
+ });
1112
+ }, [blob]);
1113
+ const handlePlay = () => {
1114
+ if (!blob) return;
1115
+ if (playing && audioRef.current) {
1116
+ audioRef.current.pause();
1117
+ audioRef.current = null;
1118
+ setPlaying(false);
1119
+ return;
1120
+ }
1121
+ const url = URL.createObjectURL(blob);
1122
+ const audio = new Audio(url);
1123
+ audio.onended = () => {
1124
+ URL.revokeObjectURL(url);
1125
+ setPlaying(false);
1126
+ audioRef.current = null;
1127
+ };
1128
+ audioRef.current = audio;
1129
+ setPlaying(true);
1130
+ audio.play();
1131
+ };
1132
+ return /* @__PURE__ */ jsxs("div", { style: {
1133
+ background: "#fff",
1134
+ border: "1px solid #e5e7eb",
1135
+ borderRadius: 8,
1136
+ padding: 12,
1137
+ display: "flex",
1138
+ alignItems: "center",
1139
+ gap: 10
1140
+ }, children: [
1141
+ /* @__PURE__ */ jsx2("button", { onClick: handlePlay, style: {
1142
+ width: 28,
1143
+ height: 28,
1144
+ borderRadius: "50%",
1145
+ border: "none",
1146
+ backgroundColor: "#1f2937",
1147
+ color: "#fff",
1148
+ fontSize: 10,
1149
+ cursor: "pointer",
1150
+ display: "flex",
1151
+ alignItems: "center",
1152
+ justifyContent: "center",
1153
+ flexShrink: 0
1154
+ }, children: playing ? "\u23F8" : "\u25B6" }),
1155
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
1156
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 1, alignItems: "center", height: 24 }, children: bars.map((level, i) => /* @__PURE__ */ jsx2("div", { style: {
1157
+ width: 2,
1158
+ height: Math.max(2, level * 22),
1159
+ backgroundColor: "#6b7280",
1160
+ borderRadius: 1
1161
+ } }, i)) }),
1162
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 10, color: "#9ca3af", marginTop: 2 }, children: formatTime(duration) })
1163
+ ] })
1164
+ ] });
1165
+ }
1166
+ function QualityBadge({ warning }) {
1167
+ if (!warning) {
1168
+ return /* @__PURE__ */ jsxs("div", { style: {
1169
+ display: "flex",
1170
+ alignItems: "center",
1171
+ gap: 6,
1172
+ padding: "6px 10px",
1173
+ background: "#f0fdf4",
1174
+ border: "1px solid #bbf7d0",
1175
+ borderRadius: 6
1176
+ }, children: [
1177
+ /* @__PURE__ */ jsx2("span", { style: { color: "#16a34a", fontSize: 12 }, children: "\u2713" }),
1178
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 11, color: "#166534" }, children: "Good quality \u2014 clear audio, low noise" })
1179
+ ] });
1180
+ }
1181
+ const isBlocking = warning.blocking;
1182
+ return /* @__PURE__ */ jsxs("div", { style: {
1183
+ display: "flex",
1184
+ alignItems: "center",
1185
+ gap: 6,
1186
+ padding: "6px 10px",
1187
+ background: isBlocking ? "#fef2f2" : "#fffbeb",
1188
+ border: `1px solid ${isBlocking ? "#fca5a5" : "#fde68a"}`,
1189
+ borderRadius: 6
1190
+ }, children: [
1191
+ /* @__PURE__ */ jsx2("span", { style: { color: isBlocking ? "#dc2626" : "#d97706", fontSize: 12 }, children: isBlocking ? "\u2717" : "\u26A0" }),
1192
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 11, color: isBlocking ? "#991b1b" : "#92400e" }, children: warning.message })
1193
+ ] });
1194
+ }
798
1195
 
799
1196
  // src/components/VoiceSettingsView.tsx
800
1197
  import { Lock, Unlock } from "lucide-react";
@@ -1505,7 +1902,7 @@ function VoiceSettingsView({ onBack, onVolumeChange }) {
1505
1902
  ) : /* @__PURE__ */ jsx3("span", {}),
1506
1903
  /* @__PURE__ */ jsxs2("span", { children: [
1507
1904
  "Kit v",
1508
- /* @__PURE__ */ jsx3("span", { style: { fontWeight: 500, color: "#6b7280" }, children: "5.0.5" })
1905
+ /* @__PURE__ */ jsx3("span", { style: { fontWeight: 500, color: "#6b7280" }, children: "5.1.0" })
1509
1906
  ] })
1510
1907
  ] }) })
1511
1908
  ]
@@ -1597,4 +1994,4 @@ export {
1597
1994
  SettingsSection,
1598
1995
  Divider
1599
1996
  };
1600
- //# sourceMappingURL=chunk-UCP5K6TJ.js.map
1997
+ //# sourceMappingURL=chunk-JUEDGAZN.js.map