mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat(bulk-base-model): implement bulk base model setting functionality with UI and context menu integration
This commit is contained in:
@@ -320,6 +320,7 @@
|
||||
"selectedSuffix": "selected",
|
||||
"viewSelected": "View Selected",
|
||||
"addTags": "Add Tags to All",
|
||||
"setBaseModel": "Set Base Model for All",
|
||||
"copyAll": "Copy All Syntax",
|
||||
"refreshAll": "Refresh All Metadata",
|
||||
"moveAll": "Move All to Folder",
|
||||
@@ -582,6 +583,14 @@
|
||||
"replaceTags": "Replace Tags",
|
||||
"saveChanges": "Save changes"
|
||||
},
|
||||
"bulkBaseModel": {
|
||||
"title": "Set Base Model for Multiple Models",
|
||||
"description": "Set base model for",
|
||||
"models": "models",
|
||||
"selectBaseModel": "Select Base Model",
|
||||
"save": "Update Base Model",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"exampleAccess": {
|
||||
"title": "Local Example Images",
|
||||
"message": "No local example images found for this model. View options:",
|
||||
@@ -989,6 +998,11 @@
|
||||
"nameUpdateFailed": "Failed to update model name",
|
||||
"baseModelUpdated": "Base model updated successfully",
|
||||
"baseModelUpdateFailed": "Failed to update base model",
|
||||
"baseModelNotSelected": "Please select a base model",
|
||||
"bulkBaseModelUpdating": "Updating base model for {count} model(s)...",
|
||||
"bulkBaseModelUpdateSuccess": "Successfully updated base model for {count} model(s)",
|
||||
"bulkBaseModelUpdatePartial": "Updated {success} model(s), failed {failed} model(s)",
|
||||
"bulkBaseModelUpdateFailed": "Failed to update base model for selected models",
|
||||
"invalidCharactersRemoved": "Invalid characters removed from filename",
|
||||
"filenameCannotBeEmpty": "File name cannot be empty",
|
||||
"renameFailed": "Failed to rename file: {message}",
|
||||
|
||||
@@ -320,6 +320,7 @@
|
||||
"selectedSuffix": "已选中",
|
||||
"viewSelected": "查看已选中",
|
||||
"addTags": "为所有添加标签",
|
||||
"setBaseModel": "为所有设置基础模型",
|
||||
"copyAll": "复制全部语法",
|
||||
"refreshAll": "刷新全部元数据",
|
||||
"moveAll": "全部移动到文件夹",
|
||||
@@ -582,6 +583,14 @@
|
||||
"replaceTags": "替换标签",
|
||||
"saveChanges": "保存更改"
|
||||
},
|
||||
"bulkBaseModel": {
|
||||
"title": "批量设置基础模型",
|
||||
"description": "为多个模型设置基础模型",
|
||||
"models": "个模型",
|
||||
"selectBaseModel": "选择基础模型",
|
||||
"save": "更新基础模型",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"exampleAccess": {
|
||||
"title": "本地示例图片",
|
||||
"message": "未找到此模型的本地示例图片。可选操作:",
|
||||
@@ -989,6 +998,11 @@
|
||||
"nameUpdateFailed": "模型名称更新失败",
|
||||
"baseModelUpdated": "基础模型更新成功",
|
||||
"baseModelUpdateFailed": "基础模型更新失败",
|
||||
"baseModelNotSelected": "请选择基础模型",
|
||||
"bulkBaseModelUpdating": "正在为 {count} 个模型更新基础模型...",
|
||||
"bulkBaseModelUpdateSuccess": "成功为 {count} 个模型更新基础模型",
|
||||
"bulkBaseModelUpdatePartial": "更新了 {success} 个模型,{failed} 个失败",
|
||||
"bulkBaseModelUpdateFailed": "为选中模型更新基础模型失败",
|
||||
"invalidCharactersRemoved": "文件名中的无效字符已移除",
|
||||
"filenameCannotBeEmpty": "文件名不能为空",
|
||||
"renameFailed": "重命名文件失败:{message}",
|
||||
|
||||
@@ -46,4 +46,62 @@
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
/* Bulk base model modal styles */
|
||||
.bulk-base-model-container {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.bulk-base-model-info {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: var(--lora-surface, #f8f9fa);
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid var(--lora-accent, #007bff);
|
||||
}
|
||||
|
||||
.bulk-base-model-info p {
|
||||
margin: 0;
|
||||
color: var(--lora-text-secondary, #6c757d);
|
||||
}
|
||||
|
||||
.bulk-base-model-selection {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.bulk-base-model-selection label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: var(--lora-text, #212529);
|
||||
}
|
||||
|
||||
.bulk-base-model-select {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--lora-border, #dee2e6);
|
||||
border-radius: 6px;
|
||||
background: var(--lora-background, #ffffff);
|
||||
color: var(--lora-text, #212529);
|
||||
font-size: 14px;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.bulk-base-model-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--lora-accent, #007bff);
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
|
||||
.bulk-base-model-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--lora-border, #dee2e6);
|
||||
}
|
||||
|
||||
.bulk-save-base-model-btn {
|
||||
min-width: 120px;
|
||||
}
|
||||
@@ -27,6 +27,7 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
|
||||
// Update button visibility based on model type
|
||||
const addTagsItem = this.menu.querySelector('[data-action="add-tags"]');
|
||||
const setBaseModelItem = this.menu.querySelector('[data-action="set-base-model"]');
|
||||
const sendToWorkflowAppendItem = this.menu.querySelector('[data-action="send-to-workflow-append"]');
|
||||
const sendToWorkflowReplaceItem = this.menu.querySelector('[data-action="send-to-workflow-replace"]');
|
||||
const copyAllItem = this.menu.querySelector('[data-action="copy-all"]');
|
||||
@@ -55,6 +56,9 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
if (addTagsItem) {
|
||||
addTagsItem.style.display = config.addTags ? 'flex' : 'none';
|
||||
}
|
||||
if (setBaseModelItem) {
|
||||
setBaseModelItem.style.display = 'flex'; // Base model editing is available for all model types
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectedCountHeader() {
|
||||
@@ -75,6 +79,9 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
case 'add-tags':
|
||||
bulkManager.showBulkAddTagsModal();
|
||||
break;
|
||||
case 'set-base-model':
|
||||
bulkManager.showBulkBaseModelModal();
|
||||
break;
|
||||
case 'send-to-workflow-append':
|
||||
bulkManager.sendAllModelsToWorkflow(false);
|
||||
break;
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
* ModelMetadata.js
|
||||
* Handles model metadata editing functionality - General version
|
||||
*/
|
||||
|
||||
import { BASE_MODEL_CATEGORIES } from '../../utils/constants.js';
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { BASE_MODELS } from '../../utils/constants.js';
|
||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
|
||||
/**
|
||||
* Set up model name editing functionality
|
||||
@@ -172,28 +172,8 @@ export function setupBaseModelEditing(filePath) {
|
||||
// Flag to track if a change was made
|
||||
let valueChanged = false;
|
||||
|
||||
// Add options from BASE_MODELS constants
|
||||
const baseModelCategories = {
|
||||
'Stable Diffusion 1.x': [BASE_MODELS.SD_1_4, BASE_MODELS.SD_1_5, BASE_MODELS.SD_1_5_LCM, BASE_MODELS.SD_1_5_HYPER],
|
||||
'Stable Diffusion 2.x': [BASE_MODELS.SD_2_0, BASE_MODELS.SD_2_1],
|
||||
'Stable Diffusion 3.x': [BASE_MODELS.SD_3, BASE_MODELS.SD_3_5, BASE_MODELS.SD_3_5_MEDIUM, BASE_MODELS.SD_3_5_LARGE, BASE_MODELS.SD_3_5_LARGE_TURBO],
|
||||
'SDXL': [BASE_MODELS.SDXL, BASE_MODELS.SDXL_LIGHTNING, BASE_MODELS.SDXL_HYPER],
|
||||
'Video Models': [
|
||||
BASE_MODELS.SVD, BASE_MODELS.LTXV, BASE_MODELS.HUNYUAN_VIDEO, BASE_MODELS.WAN_VIDEO,
|
||||
BASE_MODELS.WAN_VIDEO_1_3B_T2V, BASE_MODELS.WAN_VIDEO_14B_T2V,
|
||||
BASE_MODELS.WAN_VIDEO_14B_I2V_480P, BASE_MODELS.WAN_VIDEO_14B_I2V_720P,
|
||||
BASE_MODELS.WAN_VIDEO_2_2_TI2V_5B, BASE_MODELS.WAN_VIDEO_2_2_T2V_A14B,
|
||||
BASE_MODELS.WAN_VIDEO_2_2_I2V_A14B
|
||||
],
|
||||
'Flux Models': [BASE_MODELS.FLUX_1_D, BASE_MODELS.FLUX_1_S, BASE_MODELS.FLUX_1_KONTEXT, BASE_MODELS.FLUX_1_KREA],
|
||||
'Other Models': [
|
||||
BASE_MODELS.ILLUSTRIOUS, BASE_MODELS.PONY, BASE_MODELS.HIDREAM,
|
||||
BASE_MODELS.QWEN, BASE_MODELS.AURAFLOW,
|
||||
BASE_MODELS.PIXART_A, BASE_MODELS.PIXART_E, BASE_MODELS.HUNYUAN_1,
|
||||
BASE_MODELS.LUMINA, BASE_MODELS.KOLORS, BASE_MODELS.NOOBAI,
|
||||
BASE_MODELS.UNKNOWN
|
||||
]
|
||||
};
|
||||
// Add options from BASE_MODEL_CATEGORIES constants
|
||||
const baseModelCategories = BASE_MODEL_CATEGORIES;
|
||||
|
||||
// Create option groups for better organization
|
||||
Object.entries(baseModelCategories).forEach(([category, models]) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { updateCardsForBulkMode } from '../components/shared/ModelCard.js';
|
||||
import { modalManager } from './ModalManager.js';
|
||||
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||
import { MODEL_TYPES, MODEL_CONFIG } from '../api/apiConfig.js';
|
||||
import { PRESET_TAGS } from '../utils/constants.js';
|
||||
import { PRESET_TAGS, BASE_MODEL_CATEGORIES } from '../utils/constants.js';
|
||||
|
||||
export class BulkManager {
|
||||
constructor() {
|
||||
@@ -742,6 +742,129 @@ export class BulkManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show bulk base model modal
|
||||
*/
|
||||
showBulkBaseModelModal() {
|
||||
if (state.selectedModels.size === 0) {
|
||||
showToast('toast.models.noSelectedModels', {}, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const countElement = document.getElementById('bulkBaseModelCount');
|
||||
if (countElement) {
|
||||
countElement.textContent = state.selectedModels.size;
|
||||
}
|
||||
|
||||
modalManager.showModal('bulkBaseModelModal', null, null, () => {
|
||||
this.cleanupBulkBaseModelModal();
|
||||
});
|
||||
|
||||
// Initialize the bulk base model interface
|
||||
this.initializeBulkBaseModelInterface();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize bulk base model interface
|
||||
*/
|
||||
initializeBulkBaseModelInterface() {
|
||||
const select = document.getElementById('bulkBaseModelSelect');
|
||||
if (!select) return;
|
||||
|
||||
// Clear existing options
|
||||
select.innerHTML = '';
|
||||
|
||||
// Add placeholder option
|
||||
const placeholderOption = document.createElement('option');
|
||||
placeholderOption.value = '';
|
||||
placeholderOption.textContent = 'Select a base model...';
|
||||
placeholderOption.disabled = true;
|
||||
placeholderOption.selected = true;
|
||||
select.appendChild(placeholderOption);
|
||||
|
||||
// Create option groups for better organization
|
||||
Object.entries(BASE_MODEL_CATEGORIES).forEach(([category, models]) => {
|
||||
const optgroup = document.createElement('optgroup');
|
||||
optgroup.label = category;
|
||||
|
||||
models.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model;
|
||||
option.textContent = model;
|
||||
optgroup.appendChild(option);
|
||||
});
|
||||
|
||||
select.appendChild(optgroup);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk base model changes
|
||||
*/
|
||||
async saveBulkBaseModel() {
|
||||
const select = document.getElementById('bulkBaseModelSelect');
|
||||
if (!select || !select.value) {
|
||||
showToast('toast.models.baseModelNotSelected', {}, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const newBaseModel = select.value;
|
||||
const selectedCount = state.selectedModels.size;
|
||||
|
||||
if (selectedCount === 0) {
|
||||
showToast('toast.models.noSelectedModels', {}, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
modalManager.closeModal('bulkBaseModelModal');
|
||||
|
||||
try {
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
const errors = [];
|
||||
|
||||
// Show initial progress toast
|
||||
showToast('toast.models.bulkBaseModelUpdating', { count: selectedCount }, 'info');
|
||||
|
||||
for (const filepath of state.selectedModels) {
|
||||
try {
|
||||
await getModelApiClient().saveModelMetadata(filepath, { base_model: newBaseModel });
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
errors.push({ filepath, error: error.message });
|
||||
console.error(`Failed to update base model for ${filepath}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Show results
|
||||
if (errorCount === 0) {
|
||||
showToast('toast.models.bulkBaseModelUpdateSuccess', { count: successCount }, 'success');
|
||||
} else if (successCount > 0) {
|
||||
showToast('toast.models.bulkBaseModelUpdatePartial', {
|
||||
success: successCount,
|
||||
failed: errorCount
|
||||
}, 'warning');
|
||||
} else {
|
||||
showToast('toast.models.bulkBaseModelUpdateFailed', {}, 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during bulk base model operation:', error);
|
||||
showToast('toast.models.bulkBaseModelUpdateFailed', {}, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup bulk base model modal
|
||||
*/
|
||||
cleanupBulkBaseModelModal() {
|
||||
const select = document.getElementById('bulkBaseModelSelect');
|
||||
if (select) {
|
||||
select.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup marquee selection functionality
|
||||
*/
|
||||
|
||||
110
static/js/utils/EventManager.js
Normal file
110
static/js/utils/EventManager.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Centralized manager for handling DOM events across the application
|
||||
*/
|
||||
export class EventManager {
|
||||
constructor() {
|
||||
// Store registered handlers
|
||||
this.handlers = new Map();
|
||||
// Track active modals/states
|
||||
this.activeStates = {
|
||||
bulkMode: false,
|
||||
marqueeActive: false,
|
||||
modalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an event handler with priority
|
||||
* @param {string} eventType - The DOM event type (e.g., 'click', 'mousedown')
|
||||
* @param {string} source - Source identifier (e.g., 'bulkManager', 'contextMenu')
|
||||
* @param {Function} handler - Event handler function
|
||||
* @param {Object} options - Additional options including priority (higher number = higher priority)
|
||||
*/
|
||||
addHandler(eventType, source, handler, options = {}) {
|
||||
if (!this.handlers.has(eventType)) {
|
||||
this.handlers.set(eventType, []);
|
||||
// Set up the actual DOM listener once
|
||||
this.setupDOMListener(eventType);
|
||||
}
|
||||
|
||||
const handlerList = this.handlers.get(eventType);
|
||||
handlerList.push({
|
||||
source,
|
||||
handler,
|
||||
priority: options.priority || 0,
|
||||
options
|
||||
});
|
||||
|
||||
// Sort by priority
|
||||
handlerList.sort((a, b) => b.priority - a.priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event handler
|
||||
*/
|
||||
removeHandler(eventType, source) {
|
||||
if (!this.handlers.has(eventType)) return;
|
||||
|
||||
const handlerList = this.handlers.get(eventType);
|
||||
const newList = handlerList.filter(h => h.source !== source);
|
||||
|
||||
if (newList.length === 0) {
|
||||
// Remove the DOM listener if no handlers remain
|
||||
this.cleanupDOMListener(eventType);
|
||||
this.handlers.delete(eventType);
|
||||
} else {
|
||||
this.handlers.set(eventType, newList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup actual DOM event listener
|
||||
*/
|
||||
setupDOMListener(eventType) {
|
||||
const listener = (event) => this.handleEvent(eventType, event);
|
||||
document.addEventListener(eventType, listener);
|
||||
this._domListeners = this._domListeners || {};
|
||||
this._domListeners[eventType] = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up DOM event listener
|
||||
*/
|
||||
cleanupDOMListener(eventType) {
|
||||
if (this._domListeners && this._domListeners[eventType]) {
|
||||
document.removeEventListener(eventType, this._domListeners[eventType]);
|
||||
delete this._domListeners[eventType];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an event through registered handlers
|
||||
*/
|
||||
handleEvent(eventType, event) {
|
||||
if (!this.handlers.has(eventType)) return;
|
||||
|
||||
const handlers = this.handlers.get(eventType);
|
||||
|
||||
for (const {handler, options} of handlers) {
|
||||
// Apply conditional execution based on app state
|
||||
if (options.onlyInBulkMode && !this.activeStates.bulkMode) continue;
|
||||
if (options.onlyWhenMarqueeActive && !this.activeStates.marqueeActive) continue;
|
||||
if (options.skipWhenModalOpen && this.activeStates.modalOpen) continue;
|
||||
|
||||
// Execute handler
|
||||
const result = handler(event);
|
||||
|
||||
// Stop propagation if handler returns true
|
||||
if (result === true) break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update application state
|
||||
*/
|
||||
setState(state, value) {
|
||||
this.activeStates[state] = value;
|
||||
}
|
||||
}
|
||||
|
||||
export const eventManager = new EventManager();
|
||||
@@ -164,6 +164,29 @@ export const NODE_TYPE_ICONS = {
|
||||
// Default ComfyUI node color when bgcolor is null
|
||||
export const DEFAULT_NODE_COLOR = "#353535";
|
||||
|
||||
// Base model categories for organized selection
|
||||
export const BASE_MODEL_CATEGORIES = {
|
||||
'Stable Diffusion 1.x': [BASE_MODELS.SD_1_4, BASE_MODELS.SD_1_5, BASE_MODELS.SD_1_5_LCM, BASE_MODELS.SD_1_5_HYPER],
|
||||
'Stable Diffusion 2.x': [BASE_MODELS.SD_2_0, BASE_MODELS.SD_2_1],
|
||||
'Stable Diffusion 3.x': [BASE_MODELS.SD_3, BASE_MODELS.SD_3_5, BASE_MODELS.SD_3_5_MEDIUM, BASE_MODELS.SD_3_5_LARGE, BASE_MODELS.SD_3_5_LARGE_TURBO],
|
||||
'SDXL': [BASE_MODELS.SDXL, BASE_MODELS.SDXL_LIGHTNING, BASE_MODELS.SDXL_HYPER],
|
||||
'Video Models': [
|
||||
BASE_MODELS.SVD, BASE_MODELS.LTXV, BASE_MODELS.HUNYUAN_VIDEO, BASE_MODELS.WAN_VIDEO,
|
||||
BASE_MODELS.WAN_VIDEO_1_3B_T2V, BASE_MODELS.WAN_VIDEO_14B_T2V,
|
||||
BASE_MODELS.WAN_VIDEO_14B_I2V_480P, BASE_MODELS.WAN_VIDEO_14B_I2V_720P,
|
||||
BASE_MODELS.WAN_VIDEO_2_2_TI2V_5B, BASE_MODELS.WAN_VIDEO_2_2_T2V_A14B,
|
||||
BASE_MODELS.WAN_VIDEO_2_2_I2V_A14B
|
||||
],
|
||||
'Flux Models': [BASE_MODELS.FLUX_1_D, BASE_MODELS.FLUX_1_S, BASE_MODELS.FLUX_1_KONTEXT, BASE_MODELS.FLUX_1_KREA],
|
||||
'Other Models': [
|
||||
BASE_MODELS.ILLUSTRIOUS, BASE_MODELS.PONY, BASE_MODELS.HIDREAM,
|
||||
BASE_MODELS.QWEN, BASE_MODELS.AURAFLOW,
|
||||
BASE_MODELS.PIXART_A, BASE_MODELS.PIXART_E, BASE_MODELS.HUNYUAN_1,
|
||||
BASE_MODELS.LUMINA, BASE_MODELS.KOLORS, BASE_MODELS.NOOBAI,
|
||||
BASE_MODELS.UNKNOWN
|
||||
]
|
||||
};
|
||||
|
||||
// Preset tag suggestions
|
||||
export const PRESET_TAGS = [
|
||||
'character', 'style', 'concept', 'clothing',
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
<div class="context-menu-item" data-action="add-tags">
|
||||
<i class="fas fa-tags"></i> <span>{{ t('loras.bulkOperations.addTags') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="set-base-model">
|
||||
<i class="fas fa-layer-group"></i> <span>{{ t('loras.bulkOperations.setBaseModel') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="send-to-workflow-append">
|
||||
<i class="fas fa-paper-plane"></i> <span>{{ t('loras.contextMenu.sendToWorkflowAppend') }}</span>
|
||||
</div>
|
||||
|
||||
@@ -10,4 +10,5 @@
|
||||
{% include 'components/modals/example_access_modal.html' %}
|
||||
{% include 'components/modals/download_modal.html' %}
|
||||
{% include 'components/modals/move_modal.html' %}
|
||||
{% include 'components/modals/bulk_add_tags_modal.html' %}
|
||||
{% include 'components/modals/bulk_add_tags_modal.html' %}
|
||||
{% include 'components/modals/bulk_base_model_modal.html' %}
|
||||
31
templates/components/modals/bulk_base_model_modal.html
Normal file
31
templates/components/modals/bulk_base_model_modal.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<div id="bulkBaseModelModal" class="modal" style="display: none;">
|
||||
<div class="modal-content modal-content-large">
|
||||
<div class="modal-header">
|
||||
<h2>{{ t('modals.bulkBaseModel.title') }}</h2>
|
||||
<span class="close" onclick="modalManager.closeModal('bulkBaseModelModal')">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="bulk-base-model-info">
|
||||
<p>{{ t('modals.bulkBaseModel.description') }} <span id="bulkBaseModelCount">0</span> {{ t('modals.bulkBaseModel.models') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="bulk-base-model-container">
|
||||
<div class="bulk-base-model-selection">
|
||||
<label for="bulkBaseModelSelect">{{ t('modals.bulkBaseModel.selectBaseModel') }}</label>
|
||||
<select id="bulkBaseModelSelect" class="bulk-base-model-select">
|
||||
<!-- Options will be populated dynamically -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="bulk-base-model-controls">
|
||||
<button class="btn btn-primary bulk-save-base-model-btn" onclick="bulkManager.saveBulkBaseModel()">
|
||||
<i class="fas fa-save"></i> {{ t('modals.bulkBaseModel.save') }}
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="modalManager.closeModal('bulkBaseModelModal')">
|
||||
{{ t('modals.bulkBaseModel.cancel') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user