From 7c4c8b8f309e54cb652aef58bb8935e512a0c8d9 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Wed, 2 Sep 2026 23:15:23 +0800 Subject: [PATCH] fix(ui): add disabled state and feedback to usage tips Add button The Add button silently returned when no parameter or value was provided, looking clickable but doing nothing. Keep it disabled until both inputs are filled, validate the numeric value, surface save failures via toast without clearing user input, and confirm additions vs overwrites with success toasts. Includes translations for all locales. --- locales/de.json | 6 ++- locales/en.json | 6 ++- locales/es.json | 6 ++- locales/fr.json | 6 ++- locales/he.json | 6 ++- locales/ja.json | 6 ++- locales/ko.json | 6 ++- locales/ru.json | 6 ++- locales/zh-CN.json | 6 ++- locales/zh-TW.json | 6 ++- .../css/components/lora-modal/preset-tags.css | 9 ++++ static/js/components/shared/ModelModal.js | 41 ++++++++++++++++--- 12 files changed, 95 insertions(+), 15 deletions(-) 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