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