mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: support clip strength in LoRA usage tips, fixes #401
This commit is contained in:
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "Stärke Min",
|
||||
"strengthMax": "Stärke Max",
|
||||
"strength": "Stärke",
|
||||
"clipStrength": "Clip-Stärke",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Wert",
|
||||
"add": "Hinzufügen"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "Strength Min",
|
||||
"strengthMax": "Strength Max",
|
||||
"strength": "Strength",
|
||||
"clipStrength": "Clip Strength",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Value",
|
||||
"add": "Add"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "Fuerza mínima",
|
||||
"strengthMax": "Fuerza máxima",
|
||||
"strength": "Fuerza",
|
||||
"clipStrength": "Fuerza de Clip",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Valor",
|
||||
"add": "Añadir"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "Force Min",
|
||||
"strengthMax": "Force Max",
|
||||
"strength": "Force",
|
||||
"clipStrength": "Force Clip",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Valeur",
|
||||
"add": "Ajouter"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "強度最小",
|
||||
"strengthMax": "強度最大",
|
||||
"strength": "強度",
|
||||
"clipStrength": "クリップ強度",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "値",
|
||||
"add": "追加"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "최소 강도",
|
||||
"strengthMax": "최대 강도",
|
||||
"strength": "강도",
|
||||
"clipStrength": "클립 강도",
|
||||
"clipSkip": "클립 스킵",
|
||||
"valuePlaceholder": "값",
|
||||
"add": "추가"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "Мин. сила",
|
||||
"strengthMax": "Макс. сила",
|
||||
"strength": "Сила",
|
||||
"clipStrength": "Сила клипа",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Значение",
|
||||
"add": "Добавить"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "最小强度",
|
||||
"strengthMax": "最大强度",
|
||||
"strength": "强度",
|
||||
"clipStrength": "Clip 强度",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "数值",
|
||||
"add": "添加"
|
||||
|
||||
@@ -727,6 +727,7 @@
|
||||
"strengthMin": "最小強度",
|
||||
"strengthMax": "最大強度",
|
||||
"strength": "強度",
|
||||
"clipStrength": "Clip 強度",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "數值",
|
||||
"add": "新增"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BaseContextMenu } from './BaseContextMenu.js';
|
||||
import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
||||
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||
import { copyLoraSyntax, sendLoraToWorkflow } from '../../utils/uiHelpers.js';
|
||||
import { copyLoraSyntax, sendLoraToWorkflow, buildLoraSyntax } from '../../utils/uiHelpers.js';
|
||||
import { showExcludeModal, showDeleteModal } from '../../utils/modalUtils.js';
|
||||
import { moveManager } from '../../managers/MoveManager.js';
|
||||
|
||||
@@ -70,9 +70,8 @@ export class LoraContextMenu extends BaseContextMenu {
|
||||
sendLoraToWorkflow(replaceMode) {
|
||||
const card = this.currentCard;
|
||||
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
||||
const strength = usageTips.strength || 1;
|
||||
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
||||
|
||||
const loraSyntax = buildLoraSyntax(card.dataset.file_name, usageTips);
|
||||
|
||||
sendLoraToWorkflow(loraSyntax, replaceMode, 'lora');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { showToast, openCivitai, copyToClipboard, copyLoraSyntax, sendLoraToWorkflow, openExampleImagesFolder } from '../../utils/uiHelpers.js';
|
||||
import { showToast, openCivitai, copyToClipboard, copyLoraSyntax, sendLoraToWorkflow, openExampleImagesFolder, buildLoraSyntax } from '../../utils/uiHelpers.js';
|
||||
import { state, getCurrentPageState } from '../../state/index.js';
|
||||
import { showModelModal } from './ModelModal.js';
|
||||
import { toggleShowcase } from './showcase/ShowcaseView.js';
|
||||
@@ -155,8 +155,7 @@ async function toggleFavorite(card) {
|
||||
function handleSendToWorkflow(card, replaceMode, modelType) {
|
||||
if (modelType === MODEL_TYPES.LORA) {
|
||||
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
||||
const strength = usageTips.strength || 1;
|
||||
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
||||
const loraSyntax = buildLoraSyntax(card.dataset.file_name, usageTips);
|
||||
sendLoraToWorkflow(loraSyntax, replaceMode, 'lora');
|
||||
} else {
|
||||
// Checkpoint send functionality - to be implemented
|
||||
|
||||
@@ -271,6 +271,7 @@ function renderLoraSpecificContent(lora, escapedWords) {
|
||||
<option value="strength_min">${translate('modals.model.usageTips.strengthMin', {}, 'Strength Min')}</option>
|
||||
<option value="strength_max">${translate('modals.model.usageTips.strengthMax', {}, 'Strength Max')}</option>
|
||||
<option value="strength">${translate('modals.model.usageTips.strength', {}, '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>
|
||||
</select>
|
||||
<input type="number" id="preset-value" step="0.01" placeholder="${translate('modals.model.usageTips.valuePlaceholder', {}, 'Value')}" style="display:none;">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { state, getCurrentPageState } from '../state/index.js';
|
||||
import { showToast, copyToClipboard, sendLoraToWorkflow } from '../utils/uiHelpers.js';
|
||||
import { showToast, copyToClipboard, sendLoraToWorkflow, buildLoraSyntax } from '../utils/uiHelpers.js';
|
||||
import { updateCardsForBulkMode } from '../components/shared/ModelCard.js';
|
||||
import { modalManager } from './ModalManager.js';
|
||||
import { getModelApiClient, resetAndReload } from '../api/modelApiFactory.js';
|
||||
@@ -321,8 +321,7 @@ export class BulkManager {
|
||||
|
||||
if (metadata) {
|
||||
const usageTips = JSON.parse(metadata.usageTips || '{}');
|
||||
const strength = usageTips.strength || 1;
|
||||
loraSyntaxes.push(`<lora:${metadata.fileName}:${strength}>`);
|
||||
loraSyntaxes.push(buildLoraSyntax(metadata.fileName, usageTips));
|
||||
} else {
|
||||
missingLoras.push(filepath);
|
||||
}
|
||||
@@ -361,8 +360,7 @@ export class BulkManager {
|
||||
|
||||
if (metadata) {
|
||||
const usageTips = JSON.parse(metadata.usageTips || '{}');
|
||||
const strength = usageTips.strength || 1;
|
||||
loraSyntaxes.push(`<lora:${metadata.fileName}:${strength}>`);
|
||||
loraSyntaxes.push(buildLoraSyntax(metadata.fileName, usageTips));
|
||||
} else {
|
||||
missingLoras.push(filepath);
|
||||
}
|
||||
|
||||
@@ -295,10 +295,48 @@ export function getNSFWLevelName(level) {
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
function parseUsageTipNumber(value) {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const parsed = parseFloat(value);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getLoraStrengthsFromUsageTips(usageTips = {}) {
|
||||
const parsedStrength = parseUsageTipNumber(usageTips.strength);
|
||||
const clipStrengthSource = usageTips.clip_strength ?? usageTips.clipStrength;
|
||||
const parsedClipStrength = parseUsageTipNumber(clipStrengthSource);
|
||||
|
||||
return {
|
||||
strength: parsedStrength !== null ? parsedStrength : 1,
|
||||
hasStrength: parsedStrength !== null,
|
||||
clipStrength: parsedClipStrength,
|
||||
hasClipStrength: parsedClipStrength !== null,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildLoraSyntax(fileName, usageTips = {}) {
|
||||
const { strength, hasStrength, clipStrength, hasClipStrength } = getLoraStrengthsFromUsageTips(usageTips);
|
||||
|
||||
if (hasClipStrength) {
|
||||
const modelStrength = hasStrength ? strength : 1;
|
||||
return `<lora:${fileName}:${modelStrength}:${clipStrength}>`;
|
||||
}
|
||||
|
||||
return `<lora:${fileName}:${strength}>`;
|
||||
}
|
||||
|
||||
export function copyLoraSyntax(card) {
|
||||
const usageTips = JSON.parse(card.dataset.usage_tips || "{}");
|
||||
const strength = usageTips.strength || 1;
|
||||
const baseSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
||||
const baseSyntax = buildLoraSyntax(card.dataset.file_name, usageTips);
|
||||
|
||||
// Check if trigger words should be included
|
||||
const includeTriggerWords = state.global.settings.includeTriggerWords;
|
||||
|
||||
@@ -2,6 +2,19 @@ import { api } from "../../scripts/api.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { TextAreaCaretHelper } from "./textarea_caret_helper.js";
|
||||
|
||||
function parseUsageTipNumber(value) {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const parsed = parseFloat(value);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class AutoComplete {
|
||||
constructor(inputElement, modelType = 'loras', options = {}) {
|
||||
this.inputElement = inputElement;
|
||||
@@ -380,8 +393,10 @@ class AutoComplete {
|
||||
// Extract just the filename for LoRA name
|
||||
const fileName = relativePath.split(/[/\\]/).pop().replace(/\.(safetensors|ckpt|pt|bin)$/i, '');
|
||||
|
||||
// Get usage tips and extract strength
|
||||
// Get usage tips and extract strength information
|
||||
let strength = 1.0; // Default strength
|
||||
let hasStrength = false;
|
||||
let clipStrength = null;
|
||||
try {
|
||||
const response = await api.fetchApi(`/lm/loras/usage-tips-by-path?relative_path=${encodeURIComponent(relativePath)}`);
|
||||
if (response.ok) {
|
||||
@@ -389,8 +404,18 @@ class AutoComplete {
|
||||
if (data.success && data.usage_tips) {
|
||||
try {
|
||||
const usageTips = JSON.parse(data.usage_tips);
|
||||
if (usageTips.strength && typeof usageTips.strength === 'number') {
|
||||
strength = usageTips.strength;
|
||||
const parsedStrength = parseUsageTipNumber(usageTips.strength);
|
||||
if (parsedStrength !== null) {
|
||||
strength = parsedStrength;
|
||||
hasStrength = true;
|
||||
}
|
||||
const clipSource = usageTips.clip_strength ?? usageTips.clipStrength;
|
||||
const parsedClipStrength = parseUsageTipNumber(clipSource);
|
||||
if (parsedClipStrength !== null) {
|
||||
clipStrength = parsedClipStrength;
|
||||
if (!hasStrength) {
|
||||
strength = 1.0;
|
||||
}
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.warn('Failed to parse usage tips JSON:', parseError);
|
||||
@@ -401,8 +426,10 @@ class AutoComplete {
|
||||
console.warn('Failed to fetch usage tips:', error);
|
||||
}
|
||||
|
||||
// Format the LoRA code with strength
|
||||
const loraCode = `<lora:${fileName}:${strength}>, `;
|
||||
// Format the LoRA code with strength values
|
||||
const loraCode = clipStrength !== null
|
||||
? `<lora:${fileName}:${strength}:${clipStrength}>, `
|
||||
: `<lora:${fileName}:${strength}>, `;
|
||||
|
||||
const currentValue = this.inputElement.value;
|
||||
const caretPos = this.getCaretPosition();
|
||||
|
||||
Reference in New Issue
Block a user