diff --git a/webui/native/dist/index.html b/webui/native/dist/index.html
index 572c920e..affaa4fc 100644
--- a/webui/native/dist/index.html
+++ b/webui/native/dist/index.html
@@ -5,7 +5,7 @@
-
+
@@ -13,20 +13,20 @@
diff --git a/webui/native/src/lib/catalog.ts b/webui/native/src/lib/catalog.ts
index 393dd795..deb21996 100644
--- a/webui/native/src/lib/catalog.ts
+++ b/webui/native/src/lib/catalog.ts
@@ -18,7 +18,7 @@ interface PackageSpec {
family: string;
packages?: Array>;
options?: {
- request?: Array<{ name: string }>;
+ request?: Array<{ name: string; required?: boolean }>;
};
ui?: {
builtin_voices?: string[];
@@ -179,6 +179,9 @@ export const catalog = (rawCatalog.models as CatalogEntry[]).flatMap((entry) =>
install_packages: choices,
path: installPackage?.path || entry.path,
request_options: spec?.options?.request?.map((option) => option.name),
+ required_request_options: spec?.options?.request
+ ?.filter((option) => option.required === true)
+ .map((option) => option.name),
builtin_voices: spec?.ui?.builtin_voices,
default_voice: spec?.ui?.default_voice
}];
diff --git a/webui/native/src/lib/types.ts b/webui/native/src/lib/types.ts
index 11d8a3c0..4e58067e 100644
--- a/webui/native/src/lib/types.ts
+++ b/webui/native/src/lib/types.ts
@@ -25,6 +25,7 @@ export interface CatalogEntry {
load_options?: StringMap;
session_options?: StringMap;
request_options?: string[];
+ required_request_options?: string[];
builtin_voices?: string[];
default_voice?: string;
}
diff --git a/webui/native/src/routes/+page.svelte b/webui/native/src/routes/+page.svelte
index 46f076a5..a7f0cafd 100644
--- a/webui/native/src/routes/+page.svelte
+++ b/webui/native/src/routes/+page.svelte
@@ -258,7 +258,9 @@
!selected?.id.includes('custom');
$: referenceVoiceRequired = !quickStartVoice && (
(['clon', 'vc', 'svc'].includes(selected?.task) && selected?.family !== 'rvc') || isQwenBase);
- $: referenceTextRequired = Boolean(voiceFile) && isQwenBase;
+ $: lyricsRequired = requiresRequestOption(selected, 'lyrics');
+ $: referenceTextRequired = requiresRequestOption(selected, 'reference_text') ||
+ (Boolean(voiceFile) && isQwenBase);
$: quickStartVoices = Object.entries(demoVoiceSources)
.filter(([, source]) => bundledVoices.includes(source))
.map(([voice]) => voice);
@@ -494,6 +496,10 @@
return entry.request_options === undefined || entry.request_options.includes(option);
}
+ function requiresRequestOption(entry: CatalogEntry, option: string) {
+ return entry.required_request_options?.includes(option) === true;
+ }
+
function packageVersionLabel(size: ModelPackageSize | undefined, translate = tr) {
if (!size?.installed) return '';
if (size.version_state === 'up_to_date') return translate('models.upToDate');
@@ -1158,7 +1164,11 @@
throw new StatusWarning(`${selected.display_name_en || selected.display_name} requires a reference voice.`);
}
if (referenceTextRequired && !referenceText.trim()) {
- throw new StatusWarning('Qwen3-TTS Base voice cloning requires a reference transcript. Choose a matching .txt file or enter the transcript.');
+ const prefix = isQwenBase ? 'Qwen3-TTS Base voice cloning' : (selected.display_name_en || selected.display_name);
+ throw new StatusWarning(`${prefix} requires a reference transcript. Choose a matching .txt file or enter the transcript.`);
+ }
+ if (lyricsRequired && !lyrics.trim()) {
+ throw new StatusWarning(`${selected.display_name_en || selected.display_name} requires lyrics.`);
}
await ensureLoaded();
const options = requestOptions();
@@ -1727,8 +1737,9 @@
{/if}
{#if selected.task === 'gen'}
-
-
+
+
{/if}
{#if selected.task === 'asr'}