@stellartech/voice-widget-directus 1.0.3 → 1.0.4

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 (3) hide show
  1. package/README.md +46 -0
  2. package/dist/index.js +53 -22
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @stellartech/voice-widget-directus
2
+
3
+ Voice generation widget with model/voice selection and audio preview for Directus.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @stellartech/voice-widget-directus
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - Multiple TTS provider support (Gemini, ElevenLabs)
14
+ - Voice selection with audio sample previews
15
+ - Tone and style customization
16
+ - Full voiceover generation with progress tracking
17
+ - Async generation with background processing
18
+ - Audio playback and variant management
19
+
20
+ ## Requirements
21
+
22
+ - Directus ^11.0.0
23
+ - Vue ^3.4.0
24
+
25
+ ## Configuration
26
+
27
+ The widget requires the following collections in your Directus instance:
28
+
29
+ - `Voices` - Available voice options with samples
30
+ - `VoiceTones` - Tone presets for voice generation
31
+ - `VoiceStyles` - Style presets for voice generation
32
+ - `VoiceVariants` - Generated voice variants storage
33
+ - `AudioFiles` - Audio file records
34
+
35
+ ## Usage
36
+
37
+ After installation, the interface will be available in your Directus instance as a custom interface type. Configure it on a JSON field to enable voice generation for your content.
38
+
39
+ ### Widget Options
40
+
41
+ - **Voices Collection**: Collection containing voice definitions
42
+ - **Flow ID**: Directus Flow ID for voice generation proxy
43
+
44
+ ## License
45
+
46
+ MIT
package/dist/index.js CHANGED
@@ -530,6 +530,9 @@ function useVoicingApi(api) {
530
530
  if (!effectiveFlowId) {
531
531
  throw new Error("Voice flow ID is not configured. Set it in Widget Config or in the Flow ID field.");
532
532
  }
533
+ if (!collection) {
534
+ throw new Error("collection is required but was not provided");
535
+ }
533
536
  const voicingPayload = {
534
537
  texts: [SAMPLE_TEXT],
535
538
  provider,
@@ -538,7 +541,7 @@ function useVoicingApi(api) {
538
541
  audio_files_collection: "AudioFiles",
539
542
  // Include lesson context for callback tracking
540
543
  lesson_id: lessonId || null,
541
- collection: collection || "SM_Lessons",
544
+ collection,
542
545
  voice_config: {
543
546
  provider,
544
547
  voice_id: voiceId,
@@ -588,7 +591,10 @@ function useVoicingApi(api) {
588
591
  if (!effectiveFlowId) {
589
592
  throw new Error("Voice flow ID is not configured. Set it in Widget Config or in the Flow ID field.");
590
593
  }
591
- const collection = request.collection || "SM_Lessons";
594
+ if (!request.collection) {
595
+ throw new Error("collection is required but was not provided");
596
+ }
597
+ const collection = request.collection;
592
598
  let texts = [];
593
599
  let lessonTitle = `Lesson ${request.lessonId}`;
594
600
  try {
@@ -1037,12 +1043,15 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1037
1043
  voices.value[voiceIndex].example_status = "processing";
1038
1044
  }
1039
1045
  const effectiveFlowId = flowId.value && flowId.value.trim() ? flowId.value.trim() : void 0;
1046
+ if (!props.collection) {
1047
+ throw new Error("collection is required but was not provided");
1048
+ }
1040
1049
  generateVoiceSample(
1041
1050
  voice.voice,
1042
1051
  selectedModel.value,
1043
1052
  effectiveFlowId,
1044
1053
  props.primaryKey,
1045
- props.collection || "SM_Lessons"
1054
+ props.collection
1046
1055
  ).then(() => {
1047
1056
  console.log("[Voice Widget] Sample generation request sent for", voice.voice);
1048
1057
  }).catch((error) => {
@@ -1183,6 +1192,9 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1183
1192
  };
1184
1193
  await createPendingVoiceVariant(props.primaryKey, voiceConfig);
1185
1194
  startVoiceoverPolling();
1195
+ if (!props.collection) {
1196
+ throw new Error("collection is required but was not provided");
1197
+ }
1186
1198
  const result = await generateFullVoiceover({
1187
1199
  provider: selectedModel.value,
1188
1200
  voiceId: providerVoiceId,
@@ -1190,7 +1202,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1190
1202
  preprocessing: preprocessingEnabled.value,
1191
1203
  flowId: flowId.value,
1192
1204
  lessonId: props.primaryKey,
1193
- collection: props.collection || "SM_Lessons",
1205
+ collection: props.collection,
1194
1206
  toneId: selectedToneId.value || void 0,
1195
1207
  styleId: selectedStyleId.value || void 0
1196
1208
  });
@@ -1272,11 +1284,12 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1272
1284
  generatedAudioUrl.value = isUuid ? getAudioUrl(audioFileId) : await resolveAudioFileUrl(audioFileId);
1273
1285
  const config = variant.voice_config;
1274
1286
  if (config) {
1287
+ isRestoringFromVariant.value = true;
1275
1288
  if (config.provider) {
1276
1289
  selectedModel.value = config.provider;
1277
1290
  }
1278
1291
  if (config.voice_id) {
1279
- const matchingVoice = voices.value.find((v) => v.name === config.voice_id);
1292
+ const matchingVoice = voices.value.find((v) => v.voice === config.voice_id || v.name === config.voice_id);
1280
1293
  if (matchingVoice) {
1281
1294
  selectedVoiceId.value = matchingVoice.id;
1282
1295
  }
@@ -1290,19 +1303,27 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1290
1303
  if (typeof config.preprocessing === "boolean") {
1291
1304
  preprocessingEnabled.value = config.preprocessing;
1292
1305
  }
1306
+ isRestoringFromVariant.value = false;
1293
1307
  }
1294
1308
  }
1295
- function regenerateVoiceover() {
1309
+ async function regenerateVoiceover() {
1310
+ const variant = selectedVariant.value;
1311
+ if (variant) {
1312
+ await loadVariantAtIndex(currentVariantIndex.value);
1313
+ }
1296
1314
  generateVoiceover();
1297
1315
  }
1298
1316
  async function confirmVoiceover() {
1299
1317
  const variant = selectedVariant.value;
1300
1318
  if (!variant) return;
1301
1319
  try {
1320
+ if (!props.collection) {
1321
+ throw new Error("collection is required but was not provided");
1322
+ }
1302
1323
  await linkAudioToLesson(
1303
1324
  props.primaryKey,
1304
1325
  variant.audio_file_id,
1305
- props.collection || "SM_Lessons"
1326
+ props.collection
1306
1327
  );
1307
1328
  const value = {
1308
1329
  audio_file_id: variant.audio_file_id,
@@ -1392,20 +1413,29 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1392
1413
  console.log("[Voice Widget] Checking variants for lesson:", props.primaryKey);
1393
1414
  const variants = await fetchVoiceVariants(props.primaryKey);
1394
1415
  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);
1416
+ const processingVariants = variants.filter((v) => v.status === "processing" && !v.audio_file_id);
1417
+ if (processingVariants.length > 0) {
1418
+ console.log("[Voice Widget] Found", processingVariants.length, "processing voiceovers, showing progress");
1419
+ currentMode.value = "progress";
1420
+ processingMessage.value = "Generating voiceover...";
1421
+ progressPercent.value = 50;
1422
+ startVoiceoverPolling();
1423
+ } else {
1424
+ const completedVariants = variants.filter((v) => v.audio_file_id);
1425
+ hasExistingVoices.value = completedVariants.length > 0;
1426
+ console.log("[Voice Widget] Completed variants with audio:", completedVariants.length);
1427
+ if (completedVariants.length > 0) {
1428
+ allVariants.value = completedVariants;
1429
+ for (const v of allVariants.value) {
1430
+ if (v.audio_file_id) {
1431
+ const isUuid = v.audio_file_id?.includes("-");
1432
+ v.audioUrl = isUuid ? getAudioUrl(v.audio_file_id) : await resolveAudioFileUrl(v.audio_file_id);
1433
+ }
1404
1434
  }
1435
+ currentVariantIndex.value = 0;
1436
+ currentMode.value = "result";
1437
+ console.log("[Voice Widget] AUTO-SHOWING RESULT PAGE with", completedVariants.length, "variants");
1405
1438
  }
1406
- currentVariantIndex.value = 0;
1407
- currentMode.value = "result";
1408
- console.log("[Voice Widget] AUTO-SHOWING RESULT PAGE with", completedVariants.length, "variants");
1409
1439
  }
1410
1440
  }
1411
1441
  initFromValue();
@@ -1454,8 +1484,9 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1454
1484
  }
1455
1485
  }
1456
1486
  watch(() => props.value, initFromValue, { deep: true });
1487
+ const isRestoringFromVariant = ref(false);
1457
1488
  watch(selectedModel, () => {
1458
- if (currentVoices.value.length > 0) {
1489
+ if (!isRestoringFromVariant.value && currentVoices.value.length > 0) {
1459
1490
  selectedVoiceId.value = currentVoices.value[0].id;
1460
1491
  }
1461
1492
  });
@@ -1934,10 +1965,10 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
1934
1965
  }
1935
1966
  });
1936
1967
 
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";
1968
+ var css = "\n.voice-widget[data-v-5371543d] {\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-5371543d] {\n margin-bottom: 20px;\n}\n.widget__header-row[data-v-5371543d] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n}\n.widget__header-text[data-v-5371543d] {\n flex: 1;\n}\n.widget__title[data-v-5371543d] {\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-5371543d] {\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-5371543d]:hover {\n background: var(--theme--background-accent);\n}\n.widget__subtitle[data-v-5371543d] {\n margin: 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__header-controls[data-v-5371543d] {\n display: flex;\n gap: 16px;\n align-items: center;\n}\n.widget__url-input[data-v-5371543d] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.widget__url-label[data-v-5371543d] {\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n white-space: nowrap;\n}\n.widget__url-field[data-v-5371543d] {\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-5371543d] {\n margin-bottom: 20px;\n}\n.widget__section-label[data-v-5371543d] {\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-5371543d] {\n display: flex;\n gap: 8px;\n}\n.widget__model-btn[data-v-5371543d] {\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-5371543d]:hover {\n border-color: var(--theme--primary);\n}\n.widget__model-btn--active[data-v-5371543d] {\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-5371543d] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.widget__section--toggle[data-v-5371543d] {\n padding: 12px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n}\n.widget__toggle[data-v-5371543d] {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n}\n.widget__toggle input[data-v-5371543d] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.widget__toggle-label[data-v-5371543d] {\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__toggle-note[data-v-5371543d] {\n margin: 4px 0 0 24px;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n}\n.widget__footer[data-v-5371543d] {\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-5371543d],\n.widget__footer-right[data-v-5371543d] {\n display: flex;\n gap: 8px;\n}\n.widget__variant-nav[data-v-5371543d] {\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-5371543d] {\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-5371543d] {\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-5371543d] {\n margin-top: 8px;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n}\n.widget__btn[data-v-5371543d] {\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-5371543d]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.widget__btn--primary[data-v-5371543d] {\n background: var(--theme--primary);\n color: var(--theme--primary-foreground, #fff);\n}\n.widget__btn--primary[data-v-5371543d]:hover:not(:disabled) {\n background: var(--theme--primary-accent);\n}\n.widget__btn--secondary[data-v-5371543d] {\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-5371543d]:hover:not(:disabled) {\n background: var(--theme--background-normal);\n}\n\n/* Processing State */\n.widget__processing[data-v-5371543d] {\n padding: 40px 20px;\n text-align: center;\n}\n.widget__progress[data-v-5371543d] {\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-5371543d] {\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-5371543d] {\n height: 100%;\n background: var(--theme--primary);\n transition: width 0.3s ease;\n}\n\n/* Error State */\n.widget__error[data-v-5371543d] {\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-5371543d] {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.widget__retry[data-v-5371543d] {\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-5371543d] {\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-5371543d] {\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-5371543d] {\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-5371543d] {\n margin: 4px 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__result-info strong[data-v-5371543d] {\n color: var(--theme--foreground);\n}\n\n/* Variants List View */\n.widget__variants-list[data-v-5371543d] {\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-5371543d] {\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-5371543d]:hover {\n border-color: var(--theme--primary);\n}\n.widget__variant-item--selected[data-v-5371543d] {\n border-color: var(--theme--primary);\n background: var(--theme--primary-background);\n}\n.widget__variant-radio[data-v-5371543d] {\n flex-shrink: 0;\n}\n.widget__variant-radio input[data-v-5371543d] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.widget__variant-player[data-v-5371543d] {\n flex: 1;\n min-width: 200px;\n}\n.widget__variant-meta[data-v-5371543d] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 120px;\n}\n.widget__variant-voice[data-v-5371543d] {\n font-size: 13px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__variant-date[data-v-5371543d] {\n font-size: 11px;\n color: var(--theme--foreground-subdued);\n}\n.widget__btn--danger[data-v-5371543d] {\n color: var(--theme--danger);\n background: transparent;\n border: none;\n}\n.widget__btn--danger[data-v-5371543d]:hover {\n background: var(--theme--danger-background);\n}\n.widget__selected-info[data-v-5371543d] {\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-5371543d] {\n margin: 4px 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__selected-info strong[data-v-5371543d] {\n color: var(--theme--foreground);\n}\n.widget__progress-status[data-v-5371543d] {\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-5371543d] {\n animation: spin-5371543d 1s linear infinite;\n}\n@keyframes spin-5371543d {\nfrom { transform: rotate(0deg);\n}\nto { transform: rotate(360deg);\n}\n}\n";
1938
1969
  n(css,{});
1939
1970
 
1940
- var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-214c3276"], ["__file", "interface.vue"]]);
1971
+ var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5371543d"], ["__file", "interface.vue"]]);
1941
1972
 
1942
1973
  var index = defineInterface({
1943
1974
  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.3",
5
+ "version": "1.0.4",
6
6
  "license": "MIT",
7
7
  "readme": "README.md",
8
8
  "repository": {