mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-21 11:11:26 -03:00
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.
This commit is contained in:
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "Wert",
|
"valuePlaceholder": "Wert",
|
||||||
"add": "Hinzufügen",
|
"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": {
|
"triggerWords": {
|
||||||
"label": "Trigger Words",
|
"label": "Trigger Words",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "Value",
|
"valuePlaceholder": "Value",
|
||||||
"add": "Add",
|
"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": {
|
"triggerWords": {
|
||||||
"label": "Trigger Words",
|
"label": "Trigger Words",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "Valor",
|
"valuePlaceholder": "Valor",
|
||||||
"add": "Añadir",
|
"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": {
|
"triggerWords": {
|
||||||
"label": "Palabras clave",
|
"label": "Palabras clave",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "Valeur",
|
"valuePlaceholder": "Valeur",
|
||||||
"add": "Ajouter",
|
"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": {
|
"triggerWords": {
|
||||||
"label": "Mots-clés",
|
"label": "Mots-clés",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "ערך",
|
"valuePlaceholder": "ערך",
|
||||||
"add": "הוסף",
|
"add": "הוסף",
|
||||||
"invalidRange": "פורמט טווח לא תקין. השתמש ב-x.x-y.y"
|
"invalidRange": "פורמט טווח לא תקין. השתמש ב-x.x-y.y",
|
||||||
|
"invalidValue": "נא להזין מספר תקין",
|
||||||
|
"saveFailed": "שמירת הפרמטר הקבוע מראש נכשלה",
|
||||||
|
"added": "הפרמטר הקבוע מראש נוסף",
|
||||||
|
"updated": "הפרמטר הקבוע מראש עודכן"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
"label": "מילות טריגר",
|
"label": "מילות טריגר",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "値",
|
"valuePlaceholder": "値",
|
||||||
"add": "追加",
|
"add": "追加",
|
||||||
"invalidRange": "無効な範囲形式です。x.x-y.y を使用してください"
|
"invalidRange": "無効な範囲形式です。x.x-y.y を使用してください",
|
||||||
|
"invalidValue": "有効な数値を入力してください",
|
||||||
|
"saveFailed": "プリセットパラメータの保存に失敗しました",
|
||||||
|
"added": "プリセットパラメータを追加しました",
|
||||||
|
"updated": "プリセットパラメータを更新しました"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
"label": "トリガーワード",
|
"label": "トリガーワード",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "클립 스킵",
|
"clipSkip": "클립 스킵",
|
||||||
"valuePlaceholder": "값",
|
"valuePlaceholder": "값",
|
||||||
"add": "추가",
|
"add": "추가",
|
||||||
"invalidRange": "잘못된 범위 형식입니다. x.x-y.y를 사용하세요"
|
"invalidRange": "잘못된 범위 형식입니다. x.x-y.y를 사용하세요",
|
||||||
|
"invalidValue": "유효한 숫자를 입력하세요",
|
||||||
|
"saveFailed": "프리셋 매개변수 저장에 실패했습니다",
|
||||||
|
"added": "프리셋 매개변수가 추가되었습니다",
|
||||||
|
"updated": "프리셋 매개변수가 업데이트되었습니다"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
"label": "트리거 단어",
|
"label": "트리거 단어",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "Значение",
|
"valuePlaceholder": "Значение",
|
||||||
"add": "Добавить",
|
"add": "Добавить",
|
||||||
"invalidRange": "Неверный формат диапазона. Используйте x.x-y.y"
|
"invalidRange": "Неверный формат диапазона. Используйте x.x-y.y",
|
||||||
|
"invalidValue": "Введите корректное число",
|
||||||
|
"saveFailed": "Не удалось сохранить предустановленный параметр",
|
||||||
|
"added": "Предустановленный параметр добавлен",
|
||||||
|
"updated": "Предустановленный параметр обновлён"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
"label": "Триггерные слова",
|
"label": "Триггерные слова",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "数值",
|
"valuePlaceholder": "数值",
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
"invalidRange": "无效的范围格式。请使用 x.x-y.y"
|
"invalidRange": "无效的范围格式。请使用 x.x-y.y",
|
||||||
|
"invalidValue": "请输入有效的数值",
|
||||||
|
"saveFailed": "保存预设参数失败",
|
||||||
|
"added": "已添加预设参数",
|
||||||
|
"updated": "已更新预设参数"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
"label": "触发词",
|
"label": "触发词",
|
||||||
|
|||||||
+5
-1
@@ -1593,7 +1593,11 @@
|
|||||||
"clipSkip": "Clip Skip",
|
"clipSkip": "Clip Skip",
|
||||||
"valuePlaceholder": "數值",
|
"valuePlaceholder": "數值",
|
||||||
"add": "新增",
|
"add": "新增",
|
||||||
"invalidRange": "無效的範圍格式。請使用 x.x-y.y"
|
"invalidRange": "無效的範圍格式。請使用 x.x-y.y",
|
||||||
|
"invalidValue": "請輸入有效的數值",
|
||||||
|
"saveFailed": "儲存預設參數失敗",
|
||||||
|
"added": "已新增預設參數",
|
||||||
|
"updated": "已更新預設參數"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
"label": "觸發詞",
|
"label": "觸發詞",
|
||||||
|
|||||||
@@ -66,3 +66,12 @@
|
|||||||
.add-preset-btn:hover {
|
.add-preset-btn:hover {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-preset-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-preset-btn:hover:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
@@ -877,8 +877,9 @@ function renderLoraSpecificContent(lora, escapedWords) {
|
|||||||
<option value="clip_strength">${translate('modals.model.usageTips.clipStrength', {}, 'Clip Strength')}</option>
|
<option value="clip_strength">${translate('modals.model.usageTips.clipStrength', {}, 'Clip Strength')}</option>
|
||||||
<option value="clip_skip">${translate('modals.model.usageTips.clipSkip', {}, 'Clip Skip')}</option>
|
<option value="clip_skip">${translate('modals.model.usageTips.clipSkip', {}, 'Clip Skip')}</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="number" id="preset-value" step="0.01" placeholder="${translate('modals.model.usageTips.valuePlaceholder', {}, 'Value')}" style="display:none;">
|
<!-- autofill opt-out attrs prevent password managers / email-alias extensions from attaching popups -->
|
||||||
<button class="add-preset-btn">${translate('modals.model.usageTips.add', {}, 'Add')}</button>
|
<input type="number" id="preset-value" step="0.01" placeholder="${translate('modals.model.usageTips.valuePlaceholder', {}, 'Value')}" style="display:none;" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore data-form-type="other">
|
||||||
|
<button class="add-preset-btn" disabled>${translate('modals.model.usageTips.add', {}, 'Add')}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="preset-tags">
|
<div class="preset-tags">
|
||||||
${renderPresetTags(parsePresets(lora.usage_tips))}
|
${renderPresetTags(parsePresets(lora.usage_tips))}
|
||||||
@@ -1086,6 +1087,11 @@ function setupLoraSpecificFields(filePath) {
|
|||||||
|
|
||||||
if (!presetSelector || !presetValue || !addPresetBtn || !presetTags) return;
|
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 () {
|
presetSelector.addEventListener('change', function () {
|
||||||
const selected = this.value;
|
const selected = this.value;
|
||||||
if (selected) {
|
if (selected) {
|
||||||
@@ -1111,12 +1117,16 @@ function setupLoraSpecificFields(filePath) {
|
|||||||
} else {
|
} else {
|
||||||
presetValue.style.display = 'none';
|
presetValue.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
updateAddPresetButtonState();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
presetValue.addEventListener('input', updateAddPresetButtonState);
|
||||||
|
|
||||||
addPresetBtn.addEventListener('click', async function () {
|
addPresetBtn.addEventListener('click', async function () {
|
||||||
const key = presetSelector.value;
|
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;
|
if (!key || !value) return;
|
||||||
|
|
||||||
const currentPath = resolveFilePath();
|
const currentPath = resolveFilePath();
|
||||||
@@ -1131,9 +1141,11 @@ function setupLoraSpecificFields(filePath) {
|
|||||||
document.querySelector(`.model-card[data-filepath="${escapedFilePath}"]`);
|
document.querySelector(`.model-card[data-filepath="${escapedFilePath}"]`);
|
||||||
const currentPresets = parsePresets(loraCard?.dataset.usage_tips);
|
const currentPresets = parsePresets(loraCard?.dataset.usage_tips);
|
||||||
|
|
||||||
|
let isUpdate;
|
||||||
if (key === 'strength_range') {
|
if (key === 'strength_range') {
|
||||||
const rangeMatch = value.match(/^(-?\d*\.?\d+)\s*[-~]\s*(-?\d*\.?\d+)$/);
|
const rangeMatch = value.match(/^(-?\d*\.?\d+)\s*[-~]\s*(-?\d*\.?\d+)$/);
|
||||||
if (rangeMatch) {
|
if (rangeMatch) {
|
||||||
|
isUpdate = 'strength_min' in currentPresets || 'strength_max' in currentPresets;
|
||||||
currentPresets['strength_min'] = parseFloat(rangeMatch[1]);
|
currentPresets['strength_min'] = parseFloat(rangeMatch[1]);
|
||||||
currentPresets['strength_max'] = parseFloat(rangeMatch[2]);
|
currentPresets['strength_max'] = parseFloat(rangeMatch[2]);
|
||||||
} else {
|
} else {
|
||||||
@@ -1141,17 +1153,36 @@ function setupLoraSpecificFields(filePath) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
const newPresetsJson = JSON.stringify(currentPresets);
|
||||||
|
|
||||||
|
try {
|
||||||
await getModelApiClient().saveModelMetadata(currentPath, { usage_tips: newPresetsJson });
|
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);
|
presetTags.innerHTML = renderPresetTags(currentPresets);
|
||||||
|
showToast(
|
||||||
|
isUpdate ? 'modals.model.usageTips.updated' : 'modals.model.usageTips.added',
|
||||||
|
{},
|
||||||
|
'success',
|
||||||
|
isUpdate ? 'Preset parameter updated' : 'Preset parameter added'
|
||||||
|
);
|
||||||
|
|
||||||
presetSelector.value = '';
|
presetSelector.value = '';
|
||||||
presetValue.value = '';
|
presetValue.value = '';
|
||||||
presetValue.style.display = 'none';
|
presetValue.style.display = 'none';
|
||||||
|
addPresetBtn.disabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add keydown event for preset value
|
// Add keydown event for preset value
|
||||||
|
|||||||
Reference in New Issue
Block a user