@stellartech/voice-widget-directus 1.0.1 → 1.0.3
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/index.js +288 -122
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -455,20 +455,19 @@ const VOICE_MODELS = [
|
|
|
455
455
|
{ id: "elevenlabs", name: "ElevenLabs" }
|
|
456
456
|
];
|
|
457
457
|
const SAMPLE_TEXT = "Hello! This is a sample of my voice. I hope you enjoy listening to how I sound.";
|
|
458
|
-
const DEFAULT_FLOW_ID = "
|
|
458
|
+
const DEFAULT_FLOW_ID = "7fa08903-ed7d-4632-81fc-f422d873b8f8";
|
|
459
459
|
function useVoicingApi(api) {
|
|
460
460
|
async function fetchVoices(collection = "Voices") {
|
|
461
461
|
try {
|
|
462
462
|
const response = await api.get(`/items/${collection}`, {
|
|
463
463
|
params: {
|
|
464
|
-
filter: { status: { _eq: "published" } },
|
|
465
464
|
sort: ["sort", "name"],
|
|
466
|
-
fields: ["
|
|
465
|
+
fields: ["*"]
|
|
467
466
|
}
|
|
468
467
|
});
|
|
469
468
|
return response.data.data || [];
|
|
470
469
|
} catch (error) {
|
|
471
|
-
console.error("Failed to fetch voices:", error);
|
|
470
|
+
console.error("Failed to fetch voices:", error?.response?.data || error?.message || error);
|
|
472
471
|
return [];
|
|
473
472
|
}
|
|
474
473
|
}
|
|
@@ -486,7 +485,6 @@ function useVoicingApi(api) {
|
|
|
486
485
|
try {
|
|
487
486
|
const response = await api.get(`/items/${collection}`, {
|
|
488
487
|
params: {
|
|
489
|
-
filter: { status: { _eq: "published" } },
|
|
490
488
|
sort: ["sort", "name"],
|
|
491
489
|
fields: ["id", "name", "prompt", "sort", "is_custom"]
|
|
492
490
|
}
|
|
@@ -502,19 +500,13 @@ function useVoicingApi(api) {
|
|
|
502
500
|
return tones;
|
|
503
501
|
} catch (error) {
|
|
504
502
|
console.error("Failed to fetch tones:", error);
|
|
505
|
-
return [
|
|
506
|
-
{ id: "teacher", name: "Teacher", prompt: "Speak in a clear, educational, and patient manner", sort: 1, is_custom: false },
|
|
507
|
-
{ id: "storyteller", name: "Storyteller", prompt: "Speak in an engaging, narrative style with varied pacing", sort: 2, is_custom: false },
|
|
508
|
-
{ id: "podcaster", name: "Podcaster", prompt: "Speak in a conversational, informal yet professional tone", sort: 3, is_custom: false },
|
|
509
|
-
{ id: "other", name: "Other (Custom)", prompt: "", sort: 9999, is_custom: true }
|
|
510
|
-
];
|
|
503
|
+
return [];
|
|
511
504
|
}
|
|
512
505
|
}
|
|
513
506
|
async function fetchStyles(collection = "VoiceStyles") {
|
|
514
507
|
try {
|
|
515
508
|
const response = await api.get(`/items/${collection}`, {
|
|
516
509
|
params: {
|
|
517
|
-
filter: { status: { _eq: "published" } },
|
|
518
510
|
sort: ["sort", "name"],
|
|
519
511
|
fields: ["id", "name", "prompt", "sort", "is_custom"]
|
|
520
512
|
}
|
|
@@ -530,15 +522,10 @@ function useVoicingApi(api) {
|
|
|
530
522
|
return styles;
|
|
531
523
|
} catch (error) {
|
|
532
524
|
console.error("Failed to fetch styles:", error);
|
|
533
|
-
return [
|
|
534
|
-
{ id: "happy", name: "Happy", prompt: "enthusiastic and cheerful", sort: 1, is_custom: false },
|
|
535
|
-
{ id: "chill", name: "Chill", prompt: "relaxed and calm", sort: 2, is_custom: false },
|
|
536
|
-
{ id: "excited", name: "Excited", prompt: "energetic and animated", sort: 3, is_custom: false },
|
|
537
|
-
{ id: "other", name: "Other (Custom)", prompt: "", sort: 9999, is_custom: true }
|
|
538
|
-
];
|
|
525
|
+
return [];
|
|
539
526
|
}
|
|
540
527
|
}
|
|
541
|
-
async function generateVoiceSample(voiceId, provider, flowId = DEFAULT_FLOW_ID) {
|
|
528
|
+
async function generateVoiceSample(voiceId, provider, flowId = DEFAULT_FLOW_ID, lessonId, collection) {
|
|
542
529
|
const effectiveFlowId = flowId && flowId.trim() ? flowId.trim() : DEFAULT_FLOW_ID;
|
|
543
530
|
if (!effectiveFlowId) {
|
|
544
531
|
throw new Error("Voice flow ID is not configured. Set it in Widget Config or in the Flow ID field.");
|
|
@@ -548,7 +535,16 @@ function useVoicingApi(api) {
|
|
|
548
535
|
provider,
|
|
549
536
|
preprocessing: false,
|
|
550
537
|
title: `Voice Sample - ${voiceId}`,
|
|
551
|
-
audio_files_collection: "AudioFiles"
|
|
538
|
+
audio_files_collection: "AudioFiles",
|
|
539
|
+
// Include lesson context for callback tracking
|
|
540
|
+
lesson_id: lessonId || null,
|
|
541
|
+
collection: collection || "SM_Lessons",
|
|
542
|
+
voice_config: {
|
|
543
|
+
provider,
|
|
544
|
+
voice_id: voiceId,
|
|
545
|
+
style: "neutral",
|
|
546
|
+
is_sample: true
|
|
547
|
+
}
|
|
552
548
|
};
|
|
553
549
|
if (provider === "gemini") {
|
|
554
550
|
voicingPayload.speakers = [{ voice: voiceId, name: "Sample", style: "neutral" }];
|
|
@@ -579,7 +575,8 @@ function useVoicingApi(api) {
|
|
|
579
575
|
const url = await resolveAudioFileUrl(audioFileId);
|
|
580
576
|
return { url, audioFileId };
|
|
581
577
|
}
|
|
582
|
-
|
|
578
|
+
console.log("[Voice Widget] Sample processing async, callback will update Voices.example");
|
|
579
|
+
return { url: "", audioFileId: "" };
|
|
583
580
|
} catch (e) {
|
|
584
581
|
console.error("Failed to generate voice sample:", e);
|
|
585
582
|
throw new Error(e.response?.data?.detail || e.message || "Failed to generate sample");
|
|
@@ -685,17 +682,30 @@ function useVoicingApi(api) {
|
|
|
685
682
|
return `/assets/${audioFilesRecordId}`;
|
|
686
683
|
}
|
|
687
684
|
async function fetchVoiceVariants(lessonId) {
|
|
685
|
+
const lessonIdStr = String(lessonId);
|
|
686
|
+
console.log("[Voice Widget] ========== fetchVoiceVariants START ==========");
|
|
687
|
+
console.log("[Voice Widget] Lesson ID:", lessonIdStr);
|
|
688
|
+
console.log("[Voice Widget] API object exists:", !!api);
|
|
688
689
|
try {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
690
|
+
console.log('[Voice Widget] Calling api.get("/items/VoiceVariants")...');
|
|
691
|
+
const response = await api.get("/items/VoiceVariants");
|
|
692
|
+
console.log("[Voice Widget] Response status:", response?.status);
|
|
693
|
+
console.log("[Voice Widget] Response data type:", typeof response?.data);
|
|
694
|
+
console.log("[Voice Widget] Response.data.data type:", typeof response?.data?.data);
|
|
695
|
+
const allData = response.data?.data || response.data || [];
|
|
696
|
+
console.log("[Voice Widget] Extracted data length:", allData?.length);
|
|
697
|
+
if (allData.length > 0) {
|
|
698
|
+
console.log("[Voice Widget] First item:", JSON.stringify(allData[0]));
|
|
699
|
+
}
|
|
700
|
+
const filtered = allData.filter((v) => String(v.lesson_id) === lessonIdStr);
|
|
701
|
+
console.log("[Voice Widget] Filtered count:", filtered.length);
|
|
702
|
+
console.log("[Voice Widget] ========== fetchVoiceVariants END ==========");
|
|
703
|
+
return filtered;
|
|
697
704
|
} catch (error) {
|
|
698
|
-
console.error("
|
|
705
|
+
console.error("[Voice Widget] ========== fetchVoiceVariants ERROR ==========");
|
|
706
|
+
console.error("[Voice Widget] Error:", error);
|
|
707
|
+
console.error("[Voice Widget] Error message:", error?.message);
|
|
708
|
+
console.error("[Voice Widget] Error response:", error?.response?.status, error?.response?.data);
|
|
699
709
|
return [];
|
|
700
710
|
}
|
|
701
711
|
}
|
|
@@ -712,13 +722,13 @@ function useVoicingApi(api) {
|
|
|
712
722
|
console.warn("[Voice Widget] Could not get file UUID from AudioFiles record. fileField:", fileField);
|
|
713
723
|
return;
|
|
714
724
|
}
|
|
715
|
-
console.log(`[Voice Widget] Patching ${collection}/${voiceId} with
|
|
725
|
+
console.log(`[Voice Widget] Patching ${collection}/${voiceId} with example=${fileUuid}`);
|
|
716
726
|
await api.patch(`/items/${collection}/${voiceId}`, {
|
|
717
|
-
|
|
727
|
+
example: fileUuid
|
|
718
728
|
});
|
|
719
|
-
console.log(`[Voice Widget] Updated voice ${voiceId}
|
|
729
|
+
console.log(`[Voice Widget] Updated voice ${voiceId} example to ${fileUuid}`);
|
|
720
730
|
} catch (error) {
|
|
721
|
-
console.error("[Voice Widget] Failed to update voice
|
|
731
|
+
console.error("[Voice Widget] Failed to update voice example:", error);
|
|
722
732
|
}
|
|
723
733
|
}
|
|
724
734
|
async function deleteVoiceVariant(variantId) {
|
|
@@ -781,6 +791,59 @@ function useVoicingApi(api) {
|
|
|
781
791
|
return null;
|
|
782
792
|
}
|
|
783
793
|
}
|
|
794
|
+
async function setVoiceExampleStatus(voiceId, status, collection = "Voices") {
|
|
795
|
+
try {
|
|
796
|
+
await api.patch(`/items/${collection}/${voiceId}`, {
|
|
797
|
+
example_status: status
|
|
798
|
+
});
|
|
799
|
+
console.log(`[Voice Widget] Set voice ${voiceId} example_status to ${status}`);
|
|
800
|
+
} catch (error) {
|
|
801
|
+
console.error("[Voice Widget] Failed to set example_status:", error);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async function createPendingVoiceVariant(lessonId, voiceConfig) {
|
|
805
|
+
try {
|
|
806
|
+
const response = await api.post("/items/VoiceVariants", {
|
|
807
|
+
lesson_id: lessonId,
|
|
808
|
+
voice_config: voiceConfig,
|
|
809
|
+
status: "processing"
|
|
810
|
+
});
|
|
811
|
+
const variantId = response.data.data?.id;
|
|
812
|
+
console.log(`[Voice Widget] Created pending VoiceVariant: ${variantId}`);
|
|
813
|
+
return variantId;
|
|
814
|
+
} catch (error) {
|
|
815
|
+
console.error("[Voice Widget] Failed to create pending VoiceVariant:", error);
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
async function fetchPendingVariants(lessonId) {
|
|
820
|
+
const lessonIdStr = String(lessonId);
|
|
821
|
+
console.log("[Voice Widget] ========== fetchPendingVariants START ==========");
|
|
822
|
+
try {
|
|
823
|
+
const response = await api.get("/items/VoiceVariants", {
|
|
824
|
+
params: {
|
|
825
|
+
fields: "id,lesson_id,voice_config,status,date_created",
|
|
826
|
+
limit: -1
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
const allData = response.data?.data || [];
|
|
830
|
+
console.log("[Voice Widget] fetchPendingVariants: got", allData.length, "total variants");
|
|
831
|
+
console.log("[Voice Widget] All statuses:", allData.map((v) => v.status));
|
|
832
|
+
const pending = allData.filter((v) => {
|
|
833
|
+
const matches = String(v.lesson_id) === lessonIdStr && v.status === "processing";
|
|
834
|
+
if (matches) {
|
|
835
|
+
console.log("[Voice Widget] Found PROCESSING variant:", v.id, "status:", v.status);
|
|
836
|
+
}
|
|
837
|
+
return matches;
|
|
838
|
+
});
|
|
839
|
+
console.log("[Voice Widget] fetchPendingVariants: returning", pending.length, "processing variants");
|
|
840
|
+
console.log("[Voice Widget] ========== fetchPendingVariants END ==========");
|
|
841
|
+
return pending;
|
|
842
|
+
} catch (error) {
|
|
843
|
+
console.error("[Voice Widget] Failed to fetch pending variants:", error);
|
|
844
|
+
return [];
|
|
845
|
+
}
|
|
846
|
+
}
|
|
784
847
|
return {
|
|
785
848
|
fetchVoices,
|
|
786
849
|
fetchFlowId,
|
|
@@ -795,7 +858,10 @@ function useVoicingApi(api) {
|
|
|
795
858
|
deleteVoiceVariant,
|
|
796
859
|
createVoiceVariant,
|
|
797
860
|
linkAudioToLesson,
|
|
798
|
-
pollVoicingJob
|
|
861
|
+
pollVoicingJob,
|
|
862
|
+
setVoiceExampleStatus,
|
|
863
|
+
createPendingVoiceVariant,
|
|
864
|
+
fetchPendingVariants
|
|
799
865
|
};
|
|
800
866
|
}
|
|
801
867
|
|
|
@@ -890,14 +956,17 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
890
956
|
deleteVoiceVariant,
|
|
891
957
|
createVoiceVariant,
|
|
892
958
|
linkAudioToLesson,
|
|
893
|
-
pollVoicingJob
|
|
959
|
+
pollVoicingJob,
|
|
960
|
+
setVoiceExampleStatus,
|
|
961
|
+
createPendingVoiceVariant,
|
|
962
|
+
fetchPendingVariants
|
|
894
963
|
} = useVoicingApi(api);
|
|
895
964
|
const currentMode = ref("selection");
|
|
896
965
|
const loading = ref(true);
|
|
897
966
|
const initError = ref(null);
|
|
898
967
|
const headerExpanded = ref(!props.defaultCollapsed);
|
|
899
968
|
const savingUrl = ref(false);
|
|
900
|
-
const flowId = ref("
|
|
969
|
+
const flowId = ref("7fa08903-ed7d-4632-81fc-f422d873b8f8");
|
|
901
970
|
const selectedModel = ref("gemini");
|
|
902
971
|
const selectedVoiceId = ref(null);
|
|
903
972
|
const voices = ref([]);
|
|
@@ -918,9 +987,11 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
918
987
|
const hasExistingVoices = ref(false);
|
|
919
988
|
const allVariants = ref([]);
|
|
920
989
|
const currentVariantIndex = ref(0);
|
|
921
|
-
|
|
990
|
+
ref(null);
|
|
922
991
|
const isPollingProgress = ref(false);
|
|
923
992
|
const progressStatus = ref("");
|
|
993
|
+
let samplePollingInterval = null;
|
|
994
|
+
let voiceoverPollingInterval = null;
|
|
924
995
|
const selectedVariant = computed(() => allVariants.value[currentVariantIndex.value]);
|
|
925
996
|
const currentVoices = computed(() => {
|
|
926
997
|
return voices.value.filter((v) => v.provider === selectedModel.value);
|
|
@@ -960,24 +1031,122 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
960
1031
|
}
|
|
961
1032
|
generatingSampleFor.value = voiceId;
|
|
962
1033
|
try {
|
|
963
|
-
|
|
964
|
-
const { url, audioFileId } = await generateVoiceSample(
|
|
965
|
-
voice.voice_id,
|
|
966
|
-
selectedModel.value,
|
|
967
|
-
effectiveFlowId
|
|
968
|
-
);
|
|
969
|
-
voiceSamples.value[voiceId] = url;
|
|
970
|
-
await updateVoiceSampleFile(voiceId, audioFileId, props.voicesCollection);
|
|
1034
|
+
await setVoiceExampleStatus(voiceId, "processing", props.voicesCollection);
|
|
971
1035
|
const voiceIndex = voices.value.findIndex((v) => v.id === voiceId);
|
|
972
1036
|
if (voiceIndex !== -1) {
|
|
973
|
-
voices.value[voiceIndex].
|
|
1037
|
+
voices.value[voiceIndex].example_status = "processing";
|
|
974
1038
|
}
|
|
1039
|
+
const effectiveFlowId = flowId.value && flowId.value.trim() ? flowId.value.trim() : void 0;
|
|
1040
|
+
generateVoiceSample(
|
|
1041
|
+
voice.voice,
|
|
1042
|
+
selectedModel.value,
|
|
1043
|
+
effectiveFlowId,
|
|
1044
|
+
props.primaryKey,
|
|
1045
|
+
props.collection || "SM_Lessons"
|
|
1046
|
+
).then(() => {
|
|
1047
|
+
console.log("[Voice Widget] Sample generation request sent for", voice.voice);
|
|
1048
|
+
}).catch((error) => {
|
|
1049
|
+
console.error("[Voice Widget] Failed to start sample generation:", error?.message ?? error);
|
|
1050
|
+
setVoiceExampleStatus(voiceId, null, props.voicesCollection);
|
|
1051
|
+
});
|
|
1052
|
+
setTimeout(() => {
|
|
1053
|
+
generatingSampleFor.value = null;
|
|
1054
|
+
}, 1500);
|
|
1055
|
+
startSamplePolling();
|
|
975
1056
|
} catch (error) {
|
|
976
1057
|
console.error("[Voice Widget] Failed to generate voice:", error?.message ?? error);
|
|
977
|
-
} finally {
|
|
978
1058
|
generatingSampleFor.value = null;
|
|
979
1059
|
}
|
|
980
1060
|
}
|
|
1061
|
+
function startSamplePolling() {
|
|
1062
|
+
if (samplePollingInterval) return;
|
|
1063
|
+
console.log("[Voice Widget] Starting sample completion polling");
|
|
1064
|
+
samplePollingInterval = setInterval(async () => {
|
|
1065
|
+
try {
|
|
1066
|
+
const freshVoices = await fetchVoices(props.voicesCollection);
|
|
1067
|
+
let hasProcessing = false;
|
|
1068
|
+
for (const freshVoice of freshVoices) {
|
|
1069
|
+
const localVoice = voices.value.find((v) => v.id === freshVoice.id);
|
|
1070
|
+
if (localVoice) {
|
|
1071
|
+
if (localVoice.example_status === "processing" && freshVoice.example_status !== "processing") {
|
|
1072
|
+
console.log("[Voice Widget] Sample completed for", freshVoice.voice);
|
|
1073
|
+
localVoice.example_status = freshVoice.example_status;
|
|
1074
|
+
localVoice.example = freshVoice.example;
|
|
1075
|
+
}
|
|
1076
|
+
if (freshVoice.example_status === "processing") {
|
|
1077
|
+
hasProcessing = true;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (!hasProcessing) {
|
|
1082
|
+
console.log("[Voice Widget] All samples completed, stopping polling");
|
|
1083
|
+
stopSamplePolling();
|
|
1084
|
+
}
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
console.error("[Voice Widget] Sample polling error:", error);
|
|
1087
|
+
}
|
|
1088
|
+
}, 3e3);
|
|
1089
|
+
}
|
|
1090
|
+
function stopSamplePolling() {
|
|
1091
|
+
if (samplePollingInterval) {
|
|
1092
|
+
clearInterval(samplePollingInterval);
|
|
1093
|
+
samplePollingInterval = null;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
let voiceoverPollCount = 0;
|
|
1097
|
+
let voiceoverLessonId = null;
|
|
1098
|
+
function startVoiceoverPolling() {
|
|
1099
|
+
stopVoiceoverPolling();
|
|
1100
|
+
voiceoverPollCount = 0;
|
|
1101
|
+
voiceoverLessonId = props.primaryKey ? String(props.primaryKey) : null;
|
|
1102
|
+
if (!voiceoverLessonId) {
|
|
1103
|
+
console.error("[Voice Widget] Cannot start polling - no primaryKey!");
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
console.log("[Voice Widget] Starting voiceover completion polling for lesson:", voiceoverLessonId);
|
|
1107
|
+
const checkCompletion = async () => {
|
|
1108
|
+
if (!voiceoverLessonId) {
|
|
1109
|
+
console.error("[Voice Widget] Poll aborted - no lesson ID");
|
|
1110
|
+
stopVoiceoverPolling();
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
voiceoverPollCount++;
|
|
1114
|
+
console.log(`[Voice Widget] Poll #${voiceoverPollCount} for lesson ${voiceoverLessonId}...`);
|
|
1115
|
+
try {
|
|
1116
|
+
const allVariantsNow = await fetchVoiceVariants(voiceoverLessonId);
|
|
1117
|
+
console.log(`[Voice Widget] Poll #${voiceoverPollCount} raw result:`, JSON.stringify(allVariantsNow));
|
|
1118
|
+
const completedWithAudio = allVariantsNow.filter((v) => v.audio_file_id);
|
|
1119
|
+
console.log(`[Voice Widget] Poll #${voiceoverPollCount}: total=${allVariantsNow.length}, withAudio=${completedWithAudio.length}`);
|
|
1120
|
+
if (completedWithAudio.length > 0) {
|
|
1121
|
+
console.log("[Voice Widget] COMPLETED! Transitioning to result...");
|
|
1122
|
+
stopVoiceoverPolling();
|
|
1123
|
+
allVariants.value = completedWithAudio;
|
|
1124
|
+
for (const v of allVariants.value) {
|
|
1125
|
+
if (v.audio_file_id) {
|
|
1126
|
+
const isUuid = v.audio_file_id?.includes("-");
|
|
1127
|
+
v.audioUrl = isUuid ? getAudioUrl(v.audio_file_id) : await resolveAudioFileUrl(v.audio_file_id);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
currentVariantIndex.value = 0;
|
|
1131
|
+
hasExistingVoices.value = true;
|
|
1132
|
+
currentMode.value = "result";
|
|
1133
|
+
console.log("[Voice Widget] Mode set to result, allVariants:", allVariants.value.length);
|
|
1134
|
+
} else {
|
|
1135
|
+
console.log(`[Voice Widget] Poll #${voiceoverPollCount} - waiting for audio_file_id...`);
|
|
1136
|
+
}
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
console.error("[Voice Widget] Voiceover polling error:", error);
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
voiceoverPollingInterval = setInterval(checkCompletion, 2e3);
|
|
1142
|
+
setTimeout(checkCompletion, 500);
|
|
1143
|
+
}
|
|
1144
|
+
function stopVoiceoverPolling() {
|
|
1145
|
+
if (voiceoverPollingInterval) {
|
|
1146
|
+
clearInterval(voiceoverPollingInterval);
|
|
1147
|
+
voiceoverPollingInterval = null;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
981
1150
|
function getSelectedTonePrompt() {
|
|
982
1151
|
if (selectedToneId.value === "other") {
|
|
983
1152
|
return customTone.value;
|
|
@@ -1004,7 +1173,16 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1004
1173
|
const stylePrompt = getSelectedStylePrompt();
|
|
1005
1174
|
const combinedStyle = `${tonePrompt}. ${stylePrompt}`.trim();
|
|
1006
1175
|
const selectedVoice = voices.value.find((v) => v.id === selectedVoiceId.value);
|
|
1007
|
-
const providerVoiceId = selectedVoice?.
|
|
1176
|
+
const providerVoiceId = selectedVoice?.voice || selectedVoiceId.value;
|
|
1177
|
+
const voiceConfig = {
|
|
1178
|
+
provider: selectedModel.value,
|
|
1179
|
+
voice_id: providerVoiceId,
|
|
1180
|
+
style: combinedStyle,
|
|
1181
|
+
tone_id: selectedToneId.value,
|
|
1182
|
+
style_id: selectedStyleId.value
|
|
1183
|
+
};
|
|
1184
|
+
await createPendingVoiceVariant(props.primaryKey, voiceConfig);
|
|
1185
|
+
startVoiceoverPolling();
|
|
1008
1186
|
const result = await generateFullVoiceover({
|
|
1009
1187
|
provider: selectedModel.value,
|
|
1010
1188
|
voiceId: providerVoiceId,
|
|
@@ -1016,17 +1194,8 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1016
1194
|
toneId: selectedToneId.value || void 0,
|
|
1017
1195
|
styleId: selectedStyleId.value || void 0
|
|
1018
1196
|
});
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
voice_id: providerVoiceId,
|
|
1022
|
-
style: combinedStyle,
|
|
1023
|
-
tone_id: selectedToneId.value,
|
|
1024
|
-
style_id: selectedStyleId.value
|
|
1025
|
-
};
|
|
1026
|
-
if (result.job_id) {
|
|
1027
|
-
processingMessage.value = "Processing in background...";
|
|
1028
|
-
await startProgressPolling(result.job_id, voiceConfig);
|
|
1029
|
-
} else if (result.audio_file_id) {
|
|
1197
|
+
if (result.audio_file_id) {
|
|
1198
|
+
stopVoiceoverPolling();
|
|
1030
1199
|
generatedAudioId.value = result.audio_file_id;
|
|
1031
1200
|
generatedAudioUrl.value = getAudioUrl(result.audio_file_id);
|
|
1032
1201
|
progressPercent.value = 100;
|
|
@@ -1037,37 +1206,15 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1037
1206
|
);
|
|
1038
1207
|
await refreshVariants();
|
|
1039
1208
|
currentMode.value = "result";
|
|
1040
|
-
} else
|
|
1041
|
-
processingMessage.value = "
|
|
1042
|
-
|
|
1209
|
+
} else {
|
|
1210
|
+
processingMessage.value = "Generating voiceover...";
|
|
1211
|
+
console.log("[Voice Widget] Async generation started, polling for completion...");
|
|
1043
1212
|
}
|
|
1044
1213
|
} catch (error) {
|
|
1045
1214
|
errorMessage.value = error.message || "Failed to generate voiceover";
|
|
1046
1215
|
progressPercent.value = 0;
|
|
1047
1216
|
}
|
|
1048
1217
|
}
|
|
1049
|
-
async function pollForCompletion(callbackData, voiceConfig) {
|
|
1050
|
-
const maxAttempts = 60;
|
|
1051
|
-
const pollInterval = 5e3;
|
|
1052
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
1053
|
-
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1054
|
-
progressPercent.value = Math.min(90, 30 + i / maxAttempts * 60);
|
|
1055
|
-
try {
|
|
1056
|
-
const variants = await fetchVoiceVariants(props.primaryKey);
|
|
1057
|
-
const latest = variants.find((v) => v.callback_data === callbackData);
|
|
1058
|
-
if (latest && latest.audio_file_id) {
|
|
1059
|
-
generatedAudioId.value = latest.audio_file_id;
|
|
1060
|
-
generatedAudioUrl.value = await resolveAudioFileUrl(latest.audio_file_id);
|
|
1061
|
-
progressPercent.value = 100;
|
|
1062
|
-
currentMode.value = "result";
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
} catch (error) {
|
|
1066
|
-
console.error("Poll error:", error);
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
errorMessage.value = "Generation timed out. Please try again.";
|
|
1070
|
-
}
|
|
1071
1218
|
function retryGeneration() {
|
|
1072
1219
|
errorMessage.value = null;
|
|
1073
1220
|
generateVoiceover();
|
|
@@ -1099,37 +1246,6 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1099
1246
|
console.error("Failed to delete variant:", error);
|
|
1100
1247
|
}
|
|
1101
1248
|
}
|
|
1102
|
-
async function startProgressPolling(jobId, voiceConfig) {
|
|
1103
|
-
currentJobId.value = jobId;
|
|
1104
|
-
isPollingProgress.value = true;
|
|
1105
|
-
while (isPollingProgress.value) {
|
|
1106
|
-
const job = await pollVoicingJob(jobId);
|
|
1107
|
-
if (job) {
|
|
1108
|
-
progressPercent.value = job.progress;
|
|
1109
|
-
processingMessage.value = job.message;
|
|
1110
|
-
progressStatus.value = job.status;
|
|
1111
|
-
if (job.status === "completed") {
|
|
1112
|
-
isPollingProgress.value = false;
|
|
1113
|
-
if (job.audio_file_id) {
|
|
1114
|
-
await createVoiceVariant(
|
|
1115
|
-
props.primaryKey,
|
|
1116
|
-
job.audio_file_id,
|
|
1117
|
-
voiceConfig
|
|
1118
|
-
);
|
|
1119
|
-
}
|
|
1120
|
-
await refreshVariants();
|
|
1121
|
-
currentMode.value = "result";
|
|
1122
|
-
return;
|
|
1123
|
-
}
|
|
1124
|
-
if (job.status.startsWith("failed")) {
|
|
1125
|
-
isPollingProgress.value = false;
|
|
1126
|
-
errorMessage.value = job.message || "Generation failed";
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
1249
|
async function refreshVariants() {
|
|
1134
1250
|
const variants = await fetchVoiceVariants(props.primaryKey);
|
|
1135
1251
|
allVariants.value = variants;
|
|
@@ -1223,40 +1339,83 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1223
1339
|
}
|
|
1224
1340
|
}
|
|
1225
1341
|
async function initialize() {
|
|
1342
|
+
console.log("[Voice Widget] ===== INITIALIZE START =====");
|
|
1343
|
+
console.log("[Voice Widget] Props:", {
|
|
1344
|
+
collection: props.collection,
|
|
1345
|
+
primaryKey: props.primaryKey,
|
|
1346
|
+
voicesCollection: props.voicesCollection,
|
|
1347
|
+
tonesCollection: props.tonesCollection,
|
|
1348
|
+
stylesCollection: props.stylesCollection,
|
|
1349
|
+
flowId: props.flowId
|
|
1350
|
+
});
|
|
1226
1351
|
loading.value = true;
|
|
1227
1352
|
initError.value = null;
|
|
1228
1353
|
try {
|
|
1229
1354
|
if (!props.flowId) {
|
|
1230
1355
|
flowId.value = await fetchFlowId();
|
|
1356
|
+
console.log("[Voice Widget] Fetched flowId:", flowId.value);
|
|
1231
1357
|
} else {
|
|
1232
1358
|
flowId.value = props.flowId;
|
|
1359
|
+
console.log("[Voice Widget] Using props flowId:", flowId.value);
|
|
1233
1360
|
}
|
|
1361
|
+
console.log("[Voice Widget] Fetching voices, tones, styles...");
|
|
1234
1362
|
const [loadedVoices, loadedTones, loadedStyles] = await Promise.all([
|
|
1235
1363
|
fetchVoices(props.voicesCollection),
|
|
1236
1364
|
fetchTones(props.tonesCollection),
|
|
1237
1365
|
fetchStyles(props.stylesCollection)
|
|
1238
1366
|
]);
|
|
1367
|
+
console.log("[Voice Widget] Loaded voices:", loadedVoices.length, loadedVoices);
|
|
1368
|
+
console.log("[Voice Widget] Loaded tones:", loadedTones.length, loadedTones);
|
|
1369
|
+
console.log("[Voice Widget] Loaded styles:", loadedStyles.length, loadedStyles);
|
|
1239
1370
|
voices.value = loadedVoices;
|
|
1240
1371
|
tones.value = loadedTones;
|
|
1241
1372
|
styles.value = loadedStyles;
|
|
1373
|
+
console.log("[Voice Widget] currentVoices (filtered by model):", currentVoices.value.length);
|
|
1242
1374
|
if (currentVoices.value.length > 0 && !selectedVoiceId.value) {
|
|
1243
1375
|
selectedVoiceId.value = currentVoices.value[0].id;
|
|
1376
|
+
console.log("[Voice Widget] Auto-selected voice:", selectedVoiceId.value);
|
|
1244
1377
|
}
|
|
1245
1378
|
if (tones.value.length > 0 && !selectedToneId.value) {
|
|
1246
1379
|
selectedToneId.value = tones.value[0].id;
|
|
1380
|
+
console.log("[Voice Widget] Auto-selected tone:", selectedToneId.value);
|
|
1247
1381
|
}
|
|
1248
1382
|
if (styles.value.length > 0 && !selectedStyleId.value) {
|
|
1249
1383
|
selectedStyleId.value = styles.value[0].id;
|
|
1384
|
+
console.log("[Voice Widget] Auto-selected style:", selectedStyleId.value);
|
|
1385
|
+
}
|
|
1386
|
+
const processingVoices = voices.value.filter((v) => v.example_status === "processing");
|
|
1387
|
+
if (processingVoices.length > 0) {
|
|
1388
|
+
console.log("[Voice Widget] Found", processingVoices.length, "voices with processing samples");
|
|
1389
|
+
startSamplePolling();
|
|
1250
1390
|
}
|
|
1251
1391
|
if (props.primaryKey) {
|
|
1392
|
+
console.log("[Voice Widget] Checking variants for lesson:", props.primaryKey);
|
|
1252
1393
|
const variants = await fetchVoiceVariants(props.primaryKey);
|
|
1253
|
-
|
|
1394
|
+
console.log("[Voice Widget] All variants for lesson:", variants.length);
|
|
1395
|
+
const completedVariants = variants.filter((v) => v.audio_file_id);
|
|
1396
|
+
hasExistingVoices.value = completedVariants.length > 0;
|
|
1397
|
+
console.log("[Voice Widget] Completed variants with audio:", completedVariants.length);
|
|
1398
|
+
if (completedVariants.length > 0) {
|
|
1399
|
+
allVariants.value = completedVariants;
|
|
1400
|
+
for (const v of allVariants.value) {
|
|
1401
|
+
if (v.audio_file_id) {
|
|
1402
|
+
const isUuid = v.audio_file_id?.includes("-");
|
|
1403
|
+
v.audioUrl = isUuid ? getAudioUrl(v.audio_file_id) : await resolveAudioFileUrl(v.audio_file_id);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
currentVariantIndex.value = 0;
|
|
1407
|
+
currentMode.value = "result";
|
|
1408
|
+
console.log("[Voice Widget] AUTO-SHOWING RESULT PAGE with", completedVariants.length, "variants");
|
|
1409
|
+
}
|
|
1254
1410
|
}
|
|
1255
1411
|
initFromValue();
|
|
1412
|
+
console.log("[Voice Widget] ===== INITIALIZE SUCCESS =====");
|
|
1256
1413
|
} catch (error) {
|
|
1414
|
+
console.error("[Voice Widget] ===== INITIALIZE ERROR =====", error);
|
|
1257
1415
|
initError.value = error.message || "Failed to load configuration";
|
|
1258
1416
|
} finally {
|
|
1259
1417
|
loading.value = false;
|
|
1418
|
+
console.log("[Voice Widget] loading set to false, currentMode:", currentMode.value);
|
|
1260
1419
|
}
|
|
1261
1420
|
}
|
|
1262
1421
|
async function initFromValue() {
|
|
@@ -1300,7 +1459,14 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1300
1459
|
selectedVoiceId.value = currentVoices.value[0].id;
|
|
1301
1460
|
}
|
|
1302
1461
|
});
|
|
1303
|
-
onMounted(
|
|
1462
|
+
onMounted(() => {
|
|
1463
|
+
console.log("[Voice Widget] onMounted called");
|
|
1464
|
+
initialize();
|
|
1465
|
+
});
|
|
1466
|
+
onUnmounted(() => {
|
|
1467
|
+
stopSamplePolling();
|
|
1468
|
+
stopVoiceoverPolling();
|
|
1469
|
+
});
|
|
1304
1470
|
return (_ctx, _cache) => {
|
|
1305
1471
|
const _component_v_icon = resolveComponent("v-icon");
|
|
1306
1472
|
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
@@ -1508,8 +1674,8 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1508
1674
|
key: voice.id,
|
|
1509
1675
|
voice,
|
|
1510
1676
|
"is-selected": selectedVoiceId.value === voice.id,
|
|
1511
|
-
loading: generatingSampleFor.value === voice.id,
|
|
1512
|
-
"sample-url": voiceSamples.value[voice.id] || (voice.
|
|
1677
|
+
loading: generatingSampleFor.value === voice.id || voice.example_status === "processing",
|
|
1678
|
+
"sample-url": voiceSamples.value[voice.id] || (voice.example ? `/assets/${voice.example}` : voice.sample_url),
|
|
1513
1679
|
provider: selectedModel.value,
|
|
1514
1680
|
onSelect: selectVoice,
|
|
1515
1681
|
onGenerateVoice: generateVoice
|
|
@@ -1768,10 +1934,10 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1768
1934
|
}
|
|
1769
1935
|
});
|
|
1770
1936
|
|
|
1771
|
-
var css = "\n.voice-widget[data-v-
|
|
1937
|
+
var css = "\n.voice-widget[data-v-214c3276] {\n font-family: var(--theme--fonts--sans--font-family);\n padding: 16px;\n border: 1px solid var(--theme--form--field--input--border-color);\n border-radius: var(--theme--border-radius);\n background: var(--theme--background);\n}\n.widget__header[data-v-214c3276] {\n margin-bottom: 20px;\n}\n.widget__header-row[data-v-214c3276] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n}\n.widget__header-text[data-v-214c3276] {\n flex: 1;\n}\n.widget__title[data-v-214c3276] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 0 4px 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--theme--foreground);\n}\n.widget__collapse-btn[data-v-214c3276] {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: var(--theme--foreground-subdued);\n border-radius: 4px;\n}\n.widget__collapse-btn[data-v-214c3276]:hover {\n background: var(--theme--background-accent);\n}\n.widget__subtitle[data-v-214c3276] {\n margin: 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__header-controls[data-v-214c3276] {\n display: flex;\n gap: 16px;\n align-items: center;\n}\n.widget__url-input[data-v-214c3276] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.widget__url-label[data-v-214c3276] {\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n white-space: nowrap;\n}\n.widget__url-field[data-v-214c3276] {\n padding: 6px 10px;\n border: 1px solid var(--theme--form--field--input--border-color);\n border-radius: var(--theme--border-radius);\n font-size: 12px;\n width: 280px;\n background: var(--theme--form--field--input--background);\n color: var(--theme--foreground);\n}\n.widget__section[data-v-214c3276] {\n margin-bottom: 20px;\n}\n.widget__section-label[data-v-214c3276] {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__model-buttons[data-v-214c3276] {\n display: flex;\n gap: 8px;\n}\n.widget__model-btn[data-v-214c3276] {\n padding: 8px 16px;\n border: 1px solid var(--theme--form--field--input--border-color);\n border-radius: var(--theme--border-radius);\n background: var(--theme--background);\n color: var(--theme--foreground);\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n}\n.widget__model-btn[data-v-214c3276]:hover {\n border-color: var(--theme--primary);\n}\n.widget__model-btn--active[data-v-214c3276] {\n background: var(--theme--primary);\n border-color: var(--theme--primary);\n color: var(--theme--primary-foreground, #fff);\n}\n.widget__voice-list[data-v-214c3276] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.widget__section--toggle[data-v-214c3276] {\n padding: 12px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n}\n.widget__toggle[data-v-214c3276] {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n}\n.widget__toggle input[data-v-214c3276] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.widget__toggle-label[data-v-214c3276] {\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__toggle-note[data-v-214c3276] {\n margin: 4px 0 0 24px;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n}\n.widget__footer[data-v-214c3276] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 16px;\n border-top: 1px solid var(--theme--border-color-subdued);\n margin-top: 20px;\n}\n.widget__footer-left[data-v-214c3276],\n.widget__footer-right[data-v-214c3276] {\n display: flex;\n gap: 8px;\n}\n.widget__variant-nav[data-v-214c3276] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin-bottom: 16px;\n padding: 8px 0;\n}\n.widget__variant-counter[data-v-214c3276] {\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground-subdued);\n min-width: 60px;\n text-align: center;\n}\n.widget__btn--icon[data-v-214c3276] {\n padding: 6px;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.widget__result-date[data-v-214c3276] {\n margin-top: 8px;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n}\n.widget__btn[data-v-214c3276] {\n padding: 8px 16px;\n border: none;\n border-radius: var(--theme--border-radius);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.widget__btn[data-v-214c3276]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.widget__btn--primary[data-v-214c3276] {\n background: var(--theme--primary);\n color: var(--theme--primary-foreground, #fff);\n}\n.widget__btn--primary[data-v-214c3276]:hover:not(:disabled) {\n background: var(--theme--primary-accent);\n}\n.widget__btn--secondary[data-v-214c3276] {\n background: var(--theme--background-accent);\n color: var(--theme--foreground);\n border: 1px solid var(--theme--form--field--input--border-color);\n}\n.widget__btn--secondary[data-v-214c3276]:hover:not(:disabled) {\n background: var(--theme--background-normal);\n}\n\n/* Processing State */\n.widget__processing[data-v-214c3276] {\n padding: 40px 20px;\n text-align: center;\n}\n.widget__progress[data-v-214c3276] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin-bottom: 16px;\n font-size: 16px;\n color: var(--theme--foreground);\n}\n.widget__progress-bar[data-v-214c3276] {\n height: 8px;\n background: var(--theme--background-accent);\n border-radius: 4px;\n overflow: hidden;\n max-width: 400px;\n margin: 0 auto;\n}\n.widget__progress-fill[data-v-214c3276] {\n height: 100%;\n background: var(--theme--primary);\n transition: width 0.3s ease;\n}\n\n/* Error State */\n.widget__error[data-v-214c3276] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 20px;\n color: var(--theme--danger);\n text-align: center;\n}\n.widget__error-actions[data-v-214c3276] {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.widget__retry[data-v-214c3276] {\n padding: 6px 12px;\n background: var(--theme--danger);\n color: #fff;\n border: none;\n border-radius: var(--theme--border-radius);\n cursor: pointer;\n}\n\n/* Loading State */\n.widget__loading[data-v-214c3276] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 40px 20px;\n color: var(--theme--foreground-subdued);\n}\n\n/* Result State */\n.widget__result[data-v-214c3276] {\n padding: 20px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n margin-bottom: 20px;\n}\n.widget__result-info[data-v-214c3276] {\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--theme--border-color-subdued);\n}\n.widget__result-info p[data-v-214c3276] {\n margin: 4px 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__result-info strong[data-v-214c3276] {\n color: var(--theme--foreground);\n}\n\n/* Variants List View */\n.widget__variants-list[data-v-214c3276] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n max-height: 400px;\n overflow-y: auto;\n margin-bottom: 16px;\n}\n.widget__variant-item[data-v-214c3276] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--theme--border-color-subdued);\n border-radius: var(--theme--border-radius);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.widget__variant-item[data-v-214c3276]:hover {\n border-color: var(--theme--primary);\n}\n.widget__variant-item--selected[data-v-214c3276] {\n border-color: var(--theme--primary);\n background: var(--theme--primary-background);\n}\n.widget__variant-radio[data-v-214c3276] {\n flex-shrink: 0;\n}\n.widget__variant-radio input[data-v-214c3276] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.widget__variant-player[data-v-214c3276] {\n flex: 1;\n min-width: 200px;\n}\n.widget__variant-meta[data-v-214c3276] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 120px;\n}\n.widget__variant-voice[data-v-214c3276] {\n font-size: 13px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__variant-date[data-v-214c3276] {\n font-size: 11px;\n color: var(--theme--foreground-subdued);\n}\n.widget__btn--danger[data-v-214c3276] {\n color: var(--theme--danger);\n background: transparent;\n border: none;\n}\n.widget__btn--danger[data-v-214c3276]:hover {\n background: var(--theme--danger-background);\n}\n.widget__selected-info[data-v-214c3276] {\n padding: 12px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n margin-bottom: 16px;\n}\n.widget__selected-info p[data-v-214c3276] {\n margin: 4px 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__selected-info strong[data-v-214c3276] {\n color: var(--theme--foreground);\n}\n.widget__progress-status[data-v-214c3276] {\n text-align: center;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n margin-top: 8px;\n}\n\n/* Animations */\n.spinning[data-v-214c3276] {\n animation: spin-214c3276 1s linear infinite;\n}\n@keyframes spin-214c3276 {\nfrom { transform: rotate(0deg);\n}\nto { transform: rotate(360deg);\n}\n}\n";
|
|
1772
1938
|
n(css,{});
|
|
1773
1939
|
|
|
1774
|
-
var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1940
|
+
var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-214c3276"], ["__file", "interface.vue"]]);
|
|
1775
1941
|
|
|
1776
1942
|
var index = defineInterface({
|
|
1777
1943
|
id: "voice-widget",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stellartech/voice-widget-directus",
|
|
3
3
|
"description": "Voice generation widget with model/voice selection and audio preview for Directus",
|
|
4
4
|
"icon": "mic",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.3",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"readme": "README.md",
|
|
8
8
|
"repository": {
|