diff --git a/locales/de.json b/locales/de.json index 04d4f533..962e3bc6 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "Wert", "add": "Hinzufügen", - "invalidRange": "Ungültiges Bereichsformat. Verwenden Sie x.x-y.y" + "invalidRange": "Ungültiges Bereichsformat. Verwenden Sie x.x-y.y", + "invalidValue": "Bitte geben Sie eine gültige Zahl ein", + "saveFailed": "Fehler beim Speichern des voreingestellten Parameters", + "added": "Voreingestellter Parameter hinzugefügt", + "updated": "Voreingestellter Parameter aktualisiert" }, "triggerWords": { "label": "Trigger Words", diff --git a/locales/en.json b/locales/en.json index 2a26b773..8a2c7048 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "Value", "add": "Add", - "invalidRange": "Invalid range format. Use x.x-y.y" + "invalidRange": "Invalid range format. Use x.x-y.y", + "invalidValue": "Please enter a valid number", + "saveFailed": "Failed to save preset parameter", + "added": "Preset parameter added", + "updated": "Preset parameter updated" }, "triggerWords": { "label": "Trigger Words", diff --git a/locales/es.json b/locales/es.json index f32d6379..845e31f1 100644 --- a/locales/es.json +++ b/locales/es.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "Valor", "add": "Añadir", - "invalidRange": "Formato de rango inválido. Use x.x-y.y" + "invalidRange": "Formato de rango inválido. Use x.x-y.y", + "invalidValue": "Introduce un número válido", + "saveFailed": "Error al guardar el parámetro preajustado", + "added": "Parámetro preajustado añadido", + "updated": "Parámetro preajustado actualizado" }, "triggerWords": { "label": "Palabras clave", diff --git a/locales/fr.json b/locales/fr.json index 7f3d6f6e..f7cb0e4b 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "Valeur", "add": "Ajouter", - "invalidRange": "Format de plage invalide. Utilisez x.x-y.y" + "invalidRange": "Format de plage invalide. Utilisez x.x-y.y", + "invalidValue": "Veuillez saisir un nombre valide", + "saveFailed": "Échec de l'enregistrement du paramètre préréglé", + "added": "Paramètre préréglé ajouté", + "updated": "Paramètre préréglé mis à jour" }, "triggerWords": { "label": "Mots-clés", diff --git a/locales/he.json b/locales/he.json index f4c5414b..c1a49cb0 100644 --- a/locales/he.json +++ b/locales/he.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "ערך", "add": "הוסף", - "invalidRange": "פורמט טווח לא תקין. השתמש ב-x.x-y.y" + "invalidRange": "פורמט טווח לא תקין. השתמש ב-x.x-y.y", + "invalidValue": "נא להזין מספר תקין", + "saveFailed": "שמירת הפרמטר הקבוע מראש נכשלה", + "added": "הפרמטר הקבוע מראש נוסף", + "updated": "הפרמטר הקבוע מראש עודכן" }, "triggerWords": { "label": "מילות טריגר", diff --git a/locales/ja.json b/locales/ja.json index 1a2fec20..fc9e752f 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "値", "add": "追加", - "invalidRange": "無効な範囲形式です。x.x-y.y を使用してください" + "invalidRange": "無効な範囲形式です。x.x-y.y を使用してください", + "invalidValue": "有効な数値を入力してください", + "saveFailed": "プリセットパラメータの保存に失敗しました", + "added": "プリセットパラメータを追加しました", + "updated": "プリセットパラメータを更新しました" }, "triggerWords": { "label": "トリガーワード", diff --git a/locales/ko.json b/locales/ko.json index 5ab4aba8..90c247d8 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -1593,7 +1593,11 @@ "clipSkip": "클립 스킵", "valuePlaceholder": "값", "add": "추가", - "invalidRange": "잘못된 범위 형식입니다. x.x-y.y를 사용하세요" + "invalidRange": "잘못된 범위 형식입니다. x.x-y.y를 사용하세요", + "invalidValue": "유효한 숫자를 입력하세요", + "saveFailed": "프리셋 매개변수 저장에 실패했습니다", + "added": "프리셋 매개변수가 추가되었습니다", + "updated": "프리셋 매개변수가 업데이트되었습니다" }, "triggerWords": { "label": "트리거 단어", diff --git a/locales/ru.json b/locales/ru.json index 59ddfa80..afcbc486 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "Значение", "add": "Добавить", - "invalidRange": "Неверный формат диапазона. Используйте x.x-y.y" + "invalidRange": "Неверный формат диапазона. Используйте x.x-y.y", + "invalidValue": "Введите корректное число", + "saveFailed": "Не удалось сохранить предустановленный параметр", + "added": "Предустановленный параметр добавлен", + "updated": "Предустановленный параметр обновлён" }, "triggerWords": { "label": "Триггерные слова", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 9fe1ab22..921df39e 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "数值", "add": "添加", - "invalidRange": "无效的范围格式。请使用 x.x-y.y" + "invalidRange": "无效的范围格式。请使用 x.x-y.y", + "invalidValue": "请输入有效的数值", + "saveFailed": "保存预设参数失败", + "added": "已添加预设参数", + "updated": "已更新预设参数" }, "triggerWords": { "label": "触发词", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 78a53bb6..8e8256a7 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -1593,7 +1593,11 @@ "clipSkip": "Clip Skip", "valuePlaceholder": "數值", "add": "新增", - "invalidRange": "無效的範圍格式。請使用 x.x-y.y" + "invalidRange": "無效的範圍格式。請使用 x.x-y.y", + "invalidValue": "請輸入有效的數值", + "saveFailed": "儲存預設參數失敗", + "added": "已新增預設參數", + "updated": "已更新預設參數" }, "triggerWords": { "label": "觸發詞", diff --git a/static/css/components/lora-modal/preset-tags.css b/static/css/components/lora-modal/preset-tags.css index 1819df0c..c11d4f76 100644 --- a/static/css/components/lora-modal/preset-tags.css +++ b/static/css/components/lora-modal/preset-tags.css @@ -65,4 +65,13 @@ .add-preset-btn:hover { opacity: 0.9; +} + +.add-preset-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.add-preset-btn:hover:disabled { + opacity: 0.5; } \ No newline at end of file diff --git a/static/js/components/shared/ModelModal.js b/static/js/components/shared/ModelModal.js index 4021b9fb..81893625 100644 --- a/static/js/components/shared/ModelModal.js +++ b/static/js/components/shared/ModelModal.js @@ -877,8 +877,9 @@ function renderLoraSpecificContent(lora, escapedWords) { - - + + +
${renderPresetTags(parsePresets(lora.usage_tips))} @@ -1086,6 +1087,11 @@ function setupLoraSpecificFields(filePath) { if (!presetSelector || !presetValue || !addPresetBtn || !presetTags) return; + // Add button stays disabled until both a parameter and a value are provided + const updateAddPresetButtonState = () => { + addPresetBtn.disabled = !(presetSelector.value && presetValue.value.trim()); + }; + presetSelector.addEventListener('change', function () { const selected = this.value; if (selected) { @@ -1111,12 +1117,16 @@ function setupLoraSpecificFields(filePath) { } else { presetValue.style.display = 'none'; } + updateAddPresetButtonState(); }); + presetValue.addEventListener('input', updateAddPresetButtonState); + addPresetBtn.addEventListener('click', async function () { const key = presetSelector.value; - const value = presetValue.value; + const value = presetValue.value.trim(); + // Unreachable via UI while the button is disabled; kept as a safety net if (!key || !value) return; const currentPath = resolveFilePath(); @@ -1131,9 +1141,11 @@ function setupLoraSpecificFields(filePath) { document.querySelector(`.model-card[data-filepath="${escapedFilePath}"]`); const currentPresets = parsePresets(loraCard?.dataset.usage_tips); + let isUpdate; if (key === 'strength_range') { const rangeMatch = value.match(/^(-?\d*\.?\d+)\s*[-~]\s*(-?\d*\.?\d+)$/); if (rangeMatch) { + isUpdate = 'strength_min' in currentPresets || 'strength_max' in currentPresets; currentPresets['strength_min'] = parseFloat(rangeMatch[1]); currentPresets['strength_max'] = parseFloat(rangeMatch[2]); } else { @@ -1141,17 +1153,36 @@ function setupLoraSpecificFields(filePath) { return; } } else { - currentPresets[key] = parseFloat(value); + const numericValue = parseFloat(value); + if (!Number.isFinite(numericValue)) { + showToast('modals.model.usageTips.invalidValue', {}, 'error', 'Please enter a valid number'); + return; + } + isUpdate = key in currentPresets; + currentPresets[key] = numericValue; } const newPresetsJson = JSON.stringify(currentPresets); - await getModelApiClient().saveModelMetadata(currentPath, { usage_tips: newPresetsJson }); + try { + await getModelApiClient().saveModelMetadata(currentPath, { usage_tips: newPresetsJson }); + } catch (error) { + console.error('Failed to save preset parameter:', error); + showToast('modals.model.usageTips.saveFailed', {}, 'error', 'Failed to save preset parameter'); + return; + } presetTags.innerHTML = renderPresetTags(currentPresets); + showToast( + isUpdate ? 'modals.model.usageTips.updated' : 'modals.model.usageTips.added', + {}, + 'success', + isUpdate ? 'Preset parameter updated' : 'Preset parameter added' + ); presetSelector.value = ''; presetValue.value = ''; presetValue.style.display = 'none'; + addPresetBtn.disabled = true; }); // Add keydown event for preset value