mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 07:05:43 -03:00
feat: add setting to include trigger words in LoRA syntax, update UI and functionality, fixes #268
This commit is contained in:
@@ -555,12 +555,6 @@ export class BaseModelApiClient {
|
|||||||
|
|
||||||
async fetchModelRoots() {
|
async fetchModelRoots() {
|
||||||
try {
|
try {
|
||||||
// For checkpoints, use the specific method that considers modelType
|
|
||||||
// if (this.modelType === 'checkpoints') {
|
|
||||||
// const pageState = this.getPageState();
|
|
||||||
// return await this.fetchModelRoots(pageState.modelType || 'checkpoint');
|
|
||||||
// }
|
|
||||||
|
|
||||||
const response = await fetch(this.apiConfig.endpoints.roots);
|
const response = await fetch(this.apiConfig.endpoints.roots);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch ${this.apiConfig.config.displayName} roots`);
|
throw new Error(`Failed to fetch ${this.apiConfig.config.displayName} roots`);
|
||||||
|
|||||||
@@ -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 { copyToClipboard, sendLoraToWorkflow } from '../../utils/uiHelpers.js';
|
import { copyLoraSyntax, sendLoraToWorkflow } 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';
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export class LoraContextMenu extends BaseContextMenu {
|
|||||||
break;
|
break;
|
||||||
case 'copyname':
|
case 'copyname':
|
||||||
// Generate and copy LoRA syntax
|
// Generate and copy LoRA syntax
|
||||||
this.copyLoraSyntax();
|
copyLoraSyntax(this.currentCard);
|
||||||
break;
|
break;
|
||||||
case 'sendappend':
|
case 'sendappend':
|
||||||
// Send LoRA to workflow (append mode)
|
// Send LoRA to workflow (append mode)
|
||||||
@@ -67,16 +67,6 @@ export class LoraContextMenu extends BaseContextMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific LoRA methods
|
|
||||||
copyLoraSyntax() {
|
|
||||||
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}>`;
|
|
||||||
|
|
||||||
copyToClipboard(loraSyntax, 'LoRA syntax copied to clipboard');
|
|
||||||
}
|
|
||||||
|
|
||||||
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 || '{}');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { showToast, openCivitai, copyToClipboard, sendLoraToWorkflow, openExampleImagesFolder } from '../../utils/uiHelpers.js';
|
import { showToast, openCivitai, copyToClipboard, copyLoraSyntax, sendLoraToWorkflow, openExampleImagesFolder } 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';
|
||||||
@@ -166,10 +166,7 @@ function handleSendToWorkflow(card, replaceMode, modelType) {
|
|||||||
|
|
||||||
function handleCopyAction(card, modelType) {
|
function handleCopyAction(card, modelType) {
|
||||||
if (modelType === MODEL_TYPES.LORA) {
|
if (modelType === MODEL_TYPES.LORA) {
|
||||||
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
copyLoraSyntax(card);
|
||||||
const strength = usageTips.strength || 1;
|
|
||||||
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
|
||||||
copyToClipboard(loraSyntax, 'LoRA syntax copied to clipboard');
|
|
||||||
} else if (modelType === MODEL_TYPES.CHECKPOINT) {
|
} else if (modelType === MODEL_TYPES.CHECKPOINT) {
|
||||||
// Checkpoint copy functionality - copy checkpoint name
|
// Checkpoint copy functionality - copy checkpoint name
|
||||||
const checkpointName = card.dataset.file_name;
|
const checkpointName = card.dataset.file_name;
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ export class SettingsManager {
|
|||||||
if (state.global.settings.default_embedding_root === undefined) {
|
if (state.global.settings.default_embedding_root === undefined) {
|
||||||
state.global.settings.default_embedding_root = '';
|
state.global.settings.default_embedding_root = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set default for includeTriggerWords if undefined
|
||||||
|
if (state.global.settings.includeTriggerWords === undefined) {
|
||||||
|
state.global.settings.includeTriggerWords = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncSettingsToBackendIfNeeded() {
|
async syncSettingsToBackendIfNeeded() {
|
||||||
@@ -213,6 +218,12 @@ export class SettingsManager {
|
|||||||
this.updatePathTemplatePreview();
|
this.updatePathTemplatePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set include trigger words setting
|
||||||
|
const includeTriggerWordsCheckbox = document.getElementById('includeTriggerWords');
|
||||||
|
if (includeTriggerWordsCheckbox) {
|
||||||
|
includeTriggerWordsCheckbox.checked = state.global.settings.includeTriggerWords || false;
|
||||||
|
}
|
||||||
|
|
||||||
// Load base model path mappings
|
// Load base model path mappings
|
||||||
this.loadBaseModelMappings();
|
this.loadBaseModelMappings();
|
||||||
|
|
||||||
@@ -562,6 +573,8 @@ export class SettingsManager {
|
|||||||
state.global.settings.autoDownloadExampleImages = value;
|
state.global.settings.autoDownloadExampleImages = value;
|
||||||
} else if (settingKey === 'compact_mode') {
|
} else if (settingKey === 'compact_mode') {
|
||||||
state.global.settings.compactMode = value;
|
state.global.settings.compactMode = value;
|
||||||
|
} else if (settingKey === 'include_trigger_words') {
|
||||||
|
state.global.settings.includeTriggerWords = value;
|
||||||
} else {
|
} else {
|
||||||
// For any other settings that might be added in the future
|
// For any other settings that might be added in the future
|
||||||
state.global.settings[settingKey] = value;
|
state.global.settings[settingKey] = value;
|
||||||
@@ -587,10 +600,10 @@ export class SettingsManager {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to save setting');
|
throw new Error('Failed to save setting');
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast(`Settings updated: ${settingKey.replace(/_/g, ' ')}`, 'success');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showToast(`Settings updated: ${settingKey.replace(/_/g, ' ')}`, 'success');
|
||||||
|
|
||||||
// Apply frontend settings immediately
|
// Apply frontend settings immediately
|
||||||
this.applyFrontendSettings();
|
this.applyFrontendSettings();
|
||||||
|
|
||||||
@@ -603,7 +616,7 @@ export class SettingsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingKey === 'show_only_sfw') {
|
if (settingKey === 'show_only_sfw' || settingKey === 'blur_mature_content') {
|
||||||
this.reloadContent();
|
this.reloadContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,20 +798,13 @@ export class SettingsManager {
|
|||||||
} else if (this.currentPage === 'checkpoints') {
|
} else if (this.currentPage === 'checkpoints') {
|
||||||
// Reload the checkpoints without updating folders
|
// Reload the checkpoints without updating folders
|
||||||
await resetAndReload(false);
|
await resetAndReload(false);
|
||||||
|
} else if (this.currentPage === 'embeddings') {
|
||||||
|
// Reload the embeddings without updating folders
|
||||||
|
await resetAndReload(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFrontendSettings() {
|
applyFrontendSettings() {
|
||||||
// Apply blur setting to existing content
|
|
||||||
const blurSetting = state.global.settings.blurMatureContent;
|
|
||||||
document.querySelectorAll('.model-card[data-nsfw="true"] .card-image').forEach(img => {
|
|
||||||
if (blurSetting) {
|
|
||||||
img.classList.add('nsfw-blur');
|
|
||||||
} else {
|
|
||||||
img.classList.remove('nsfw-blur');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Apply autoplay setting to existing videos in card previews
|
// Apply autoplay setting to existing videos in card previews
|
||||||
const autoplayOnHover = state.global.settings.autoplayOnHover;
|
const autoplayOnHover = state.global.settings.autoplayOnHover;
|
||||||
document.querySelectorAll('.card-preview video').forEach(video => {
|
document.querySelectorAll('.card-preview video').forEach(video => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getCurrentPageState } from '../state/index.js';
|
import { state, getCurrentPageState } from '../state/index.js';
|
||||||
import { getStorageItem, setStorageItem } from './storageHelpers.js';
|
import { getStorageItem, setStorageItem } from './storageHelpers.js';
|
||||||
import { NODE_TYPE_ICONS, DEFAULT_NODE_COLOR } from './constants.js';
|
import { NODE_TYPE_ICONS, DEFAULT_NODE_COLOR } from './constants.js';
|
||||||
|
|
||||||
@@ -285,6 +285,76 @@ export function getNSFWLevelName(level) {
|
|||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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}>`;
|
||||||
|
|
||||||
|
// Check if trigger words should be included
|
||||||
|
const includeTriggerWords = state.global.settings.includeTriggerWords;
|
||||||
|
|
||||||
|
if (!includeTriggerWords) {
|
||||||
|
copyToClipboard(baseSyntax, "LoRA syntax copied to clipboard");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get trigger words from metadata
|
||||||
|
const meta = card.dataset.meta ? JSON.parse(card.dataset.meta) : null;
|
||||||
|
const trainedWords = meta?.trainedWords;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!trainedWords ||
|
||||||
|
!Array.isArray(trainedWords) ||
|
||||||
|
trainedWords.length === 0
|
||||||
|
) {
|
||||||
|
copyToClipboard(
|
||||||
|
baseSyntax,
|
||||||
|
"LoRA syntax copied to clipboard (no trigger words found)"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let finalSyntax = baseSyntax;
|
||||||
|
|
||||||
|
if (trainedWords.length === 1) {
|
||||||
|
// Single group: append trigger words to the same line
|
||||||
|
const triggers = trainedWords[0]
|
||||||
|
.split(",")
|
||||||
|
.map((word) => word.trim())
|
||||||
|
.filter((word) => word);
|
||||||
|
if (triggers.length > 0) {
|
||||||
|
finalSyntax = `${baseSyntax}, ${triggers.join(", ")}`;
|
||||||
|
}
|
||||||
|
copyToClipboard(
|
||||||
|
finalSyntax,
|
||||||
|
"LoRA syntax with trigger words copied to clipboard"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Multiple groups: format with separators
|
||||||
|
const groups = trainedWords
|
||||||
|
.map((group) => {
|
||||||
|
const triggers = group
|
||||||
|
.split(",")
|
||||||
|
.map((word) => word.trim())
|
||||||
|
.filter((word) => word);
|
||||||
|
return triggers.join(", ");
|
||||||
|
})
|
||||||
|
.filter((group) => group);
|
||||||
|
|
||||||
|
if (groups.length > 0) {
|
||||||
|
// Use separator between all groups except the first
|
||||||
|
finalSyntax = baseSyntax + ", " + groups[0];
|
||||||
|
for (let i = 1; i < groups.length; i++) {
|
||||||
|
finalSyntax += `\n${"-".repeat(17)}\n${groups[i]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copyToClipboard(
|
||||||
|
finalSyntax,
|
||||||
|
"LoRA syntax with trigger word groups copied to clipboard"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends LoRA syntax to the active ComfyUI workflow
|
* Sends LoRA syntax to the active ComfyUI workflow
|
||||||
* @param {string} loraSyntax - The LoRA syntax to send
|
* @param {string} loraSyntax - The LoRA syntax to send
|
||||||
|
|||||||
@@ -308,6 +308,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Misc. Section -->
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3>Misc.</h3>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="includeTriggerWords">Include Trigger Words in LoRA Syntax</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="includeTriggerWords"
|
||||||
|
onchange="settingsManager.saveToggleSetting('includeTriggerWords', 'include_trigger_words')">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-help">
|
||||||
|
Include trained trigger words when copying LoRA syntax to clipboard
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user