@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.
- package/dist/{VoiceSettingsView-OHBSNGY7.js → VoiceSettingsView-ZONLN45Z.js} +2 -2
- package/dist/{chunk-UCP5K6TJ.js → chunk-JUEDGAZN.js} +416 -19
- package/dist/chunk-JUEDGAZN.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +222 -23
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-UCP5K6TJ.js.map +0 -1
- /package/dist/{VoiceSettingsView-OHBSNGY7.js.map → VoiceSettingsView-ZONLN45Z.js.map} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
SliderSetting,
|
|
6
6
|
ToggleSetting,
|
|
7
7
|
VoiceSettingsView
|
|
8
|
-
} from "./chunk-
|
|
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-
|
|
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.
|
|
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__ */
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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-
|
|
1997
|
+
//# sourceMappingURL=chunk-JUEDGAZN.js.map
|