Files
ComfyUI-Lora-Manager/static/js/components/ContextMenu/LoraContextMenu.js
Will Miao 0ecbdf6f39 feat(context-menu): prevent duplicate NSFW selector initialization
Add initialization tracking to prevent multiple event listener attachments
in context menu components. Use dataset.initialized flag to ensure NSFW
selector events are only set up once per component instance.

In ModelContextMenuMixin, replace DOM elements and reattach event listeners
to avoid duplicates when components are reinitialized. This fixes issues
where multiple click handlers could be attached to the same elements.
2025-10-17 10:52:02 +08:00

81 lines
3.2 KiB
JavaScript

import { BaseContextMenu } from './BaseContextMenu.js';
import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
import { copyLoraSyntax, sendLoraToWorkflow, buildLoraSyntax } from '../../utils/uiHelpers.js';
import { showExcludeModal, showDeleteModal } from '../../utils/modalUtils.js';
import { moveManager } from '../../managers/MoveManager.js';
export class LoraContextMenu extends BaseContextMenu {
constructor() {
super('loraContextMenu', '.model-card');
this.nsfwSelector = document.getElementById('nsfwLevelSelector');
this.modelType = 'lora';
this.resetAndReload = resetAndReload;
// Initialize NSFW Level Selector events only if not already initialized
if (this.nsfwSelector && !this.nsfwSelector.dataset.initialized) {
this.initNSFWSelector();
this.nsfwSelector.dataset.initialized = 'true';
}
}
// Use the saveModelMetadata implementation from loraApi
async saveModelMetadata(filePath, data) {
return getModelApiClient().saveModelMetadata(filePath, data);
}
handleMenuAction(action, menuItem) {
// First try to handle with common actions
if (ModelContextMenuMixin.handleCommonMenuActions.call(this, action)) {
return;
}
// Otherwise handle lora-specific actions
switch(action) {
case 'detail':
// Trigger the main card click which shows the modal
this.currentCard.click();
break;
case 'copyname':
// Generate and copy LoRA syntax
copyLoraSyntax(this.currentCard);
break;
case 'sendappend':
// Send LoRA to workflow (append mode)
this.sendLoraToWorkflow(false);
break;
case 'sendreplace':
// Send LoRA to workflow (replace mode)
this.sendLoraToWorkflow(true);
break;
case 'replace-preview':
// Add a new action for replacing preview images
getModelApiClient().replaceModelPreview(this.currentCard.dataset.filepath);
break;
case 'delete':
// Call showDeleteModal directly instead of clicking the trash button
showDeleteModal(this.currentCard.dataset.filepath);
break;
case 'move':
moveManager.showMoveModal(this.currentCard.dataset.filepath);
break;
case 'refresh-metadata':
getModelApiClient().refreshSingleModelMetadata(this.currentCard.dataset.filepath);
break;
case 'exclude':
showExcludeModal(this.currentCard.dataset.filepath);
break;
}
}
sendLoraToWorkflow(replaceMode) {
const card = this.currentCard;
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
const loraSyntax = buildLoraSyntax(card.dataset.file_name, usageTips);
sendLoraToWorkflow(loraSyntax, replaceMode, 'lora');
}
}
// Mix in shared methods
Object.assign(LoraContextMenu.prototype, ModelContextMenuMixin);