mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-22 11:21:15 -03:00
Compare commits
2 Commits
5d3ab3bbf8
...
75f74d54d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75f74d54d8 | ||
|
|
ab6100f596 |
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "Aus Favoriten entfernen",
|
||||
"deleteAll": "Ausgewählte löschen",
|
||||
"downloadMissingLoras": "Fehlende LoRAs herunterladen",
|
||||
"downloadExamples": "Beispielbilder herunterladen",
|
||||
"clear": "Auswahl löschen",
|
||||
"skipMetadataRefreshCount": "Überspringen({count} Modelle)",
|
||||
"resumeMetadataRefreshCount": "Fortsetzen({count} Modelle)",
|
||||
"sendToWorkflow": "An Workflow senden",
|
||||
"sections": {
|
||||
"workflow": "Workflow",
|
||||
"metadata": "Metadaten",
|
||||
"attributes": "Attribute",
|
||||
"organize": "Organisieren",
|
||||
"download": "Download"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "Automatische Organisation wird initialisiert...",
|
||||
"starting": "Automatische Organisation für {type} wird gestartet...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "Remove from Favorites",
|
||||
"deleteAll": "Delete Selected",
|
||||
"downloadMissingLoras": "Download Missing LoRAs",
|
||||
"downloadExamples": "Download Example Images",
|
||||
"clear": "Clear Selection",
|
||||
"skipMetadataRefreshCount": "Skip ({count} models)",
|
||||
"resumeMetadataRefreshCount": "Resume ({count} models)",
|
||||
"sendToWorkflow": "Send to Workflow",
|
||||
"sections": {
|
||||
"workflow": "Workflow",
|
||||
"metadata": "Metadata",
|
||||
"attributes": "Attributes",
|
||||
"organize": "Organize",
|
||||
"download": "Download"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "Initializing auto-organize...",
|
||||
"starting": "Starting auto-organize for {type}...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "Quitar de favoritos",
|
||||
"deleteAll": "Eliminar seleccionados",
|
||||
"downloadMissingLoras": "Descargar LoRAs faltantes",
|
||||
"downloadExamples": "Descargar imágenes de ejemplo",
|
||||
"clear": "Limpiar selección",
|
||||
"skipMetadataRefreshCount": "Omitir({count} modelos)",
|
||||
"resumeMetadataRefreshCount": "Reanudar({count} modelos)",
|
||||
"sendToWorkflow": "Enviar al workflow",
|
||||
"sections": {
|
||||
"workflow": "Workflow",
|
||||
"metadata": "Metadatos",
|
||||
"attributes": "Atributos",
|
||||
"organize": "Organizar",
|
||||
"download": "Descargar"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "Inicializando auto-organización...",
|
||||
"starting": "Iniciando auto-organización para {type}...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "Retirer des favoris",
|
||||
"deleteAll": "Supprimer la sélection",
|
||||
"downloadMissingLoras": "Télécharger les LoRAs manquants",
|
||||
"downloadExamples": "Télécharger les images d'exemple",
|
||||
"clear": "Effacer la sélection",
|
||||
"skipMetadataRefreshCount": "Ignorer({count} modèles)",
|
||||
"resumeMetadataRefreshCount": "Reprendre({count} modèles)",
|
||||
"sendToWorkflow": "Envoyer au workflow",
|
||||
"sections": {
|
||||
"workflow": "Workflow",
|
||||
"metadata": "Métadonnées",
|
||||
"attributes": "Attributs",
|
||||
"organize": "Organiser",
|
||||
"download": "Télécharger"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "Initialisation de l'auto-organisation...",
|
||||
"starting": "Démarrage de l'auto-organisation pour {type}...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "הסר ממועדפים",
|
||||
"deleteAll": "מחק נבחרים",
|
||||
"downloadMissingLoras": "הורדת LoRAs חסרים",
|
||||
"downloadExamples": "הורד תמונות דוגמה",
|
||||
"clear": "נקה בחירה",
|
||||
"skipMetadataRefreshCount": "דילוג({count} מודלים)",
|
||||
"resumeMetadataRefreshCount": "המשך({count} מודלים)",
|
||||
"sendToWorkflow": "שלח ל-Workflow",
|
||||
"sections": {
|
||||
"workflow": "Workflow",
|
||||
"metadata": "מטא-נתונים",
|
||||
"attributes": "מאפיינים",
|
||||
"organize": "ארגן",
|
||||
"download": "הורדה"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "מאתחל ארגון אוטומטי...",
|
||||
"starting": "מתחיל ארגון אוטומטי עבור {type}...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "お気に入りから削除",
|
||||
"deleteAll": "選択したものを削除",
|
||||
"downloadMissingLoras": "不足している LoRA をダウンロード",
|
||||
"downloadExamples": "例画像をダウンロード",
|
||||
"clear": "選択をクリア",
|
||||
"skipMetadataRefreshCount": "スキップ({count}モデル)",
|
||||
"resumeMetadataRefreshCount": "再開({count}モデル)",
|
||||
"sendToWorkflow": "ワークフローに送信",
|
||||
"sections": {
|
||||
"workflow": "ワークフロー",
|
||||
"metadata": "メタデータ",
|
||||
"attributes": "属性",
|
||||
"organize": "整理",
|
||||
"download": "ダウンロード"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "自動整理を初期化中...",
|
||||
"starting": "{type}の自動整理を開始中...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "즐겨찾기 해제",
|
||||
"deleteAll": "선택된 항목 삭제",
|
||||
"downloadMissingLoras": "누락된 LoRA 다운로드",
|
||||
"downloadExamples": "예시 이미지 다운로드",
|
||||
"clear": "선택 지우기",
|
||||
"skipMetadataRefreshCount": "건너뛰기({count}개 모델)",
|
||||
"resumeMetadataRefreshCount": "재개({count}개 모델)",
|
||||
"sendToWorkflow": "워크플로우로 보내기",
|
||||
"sections": {
|
||||
"workflow": "워크플로우",
|
||||
"metadata": "메타데이터",
|
||||
"attributes": "속성",
|
||||
"organize": "정리",
|
||||
"download": "다운로드"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "자동 정리 초기화 중...",
|
||||
"starting": "{type}에 대한 자동 정리 시작...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "Удалить из избранного",
|
||||
"deleteAll": "Удалить выбранные",
|
||||
"downloadMissingLoras": "Скачать отсутствующие LoRAs",
|
||||
"downloadExamples": "Загрузить примеры изображений",
|
||||
"clear": "Очистить выбор",
|
||||
"skipMetadataRefreshCount": "Пропустить({count} моделей)",
|
||||
"resumeMetadataRefreshCount": "Возобновить({count} моделей)",
|
||||
"sendToWorkflow": "Отправить в Workflow",
|
||||
"sections": {
|
||||
"workflow": "Workflow",
|
||||
"metadata": "Метаданные",
|
||||
"attributes": "Атрибуты",
|
||||
"organize": "Организовать",
|
||||
"download": "Скачать"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "Инициализация автоматической организации...",
|
||||
"starting": "Запуск автоматической организации для {type}...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "取消收藏",
|
||||
"deleteAll": "删除已选",
|
||||
"downloadMissingLoras": "下载缺失的 LoRAs",
|
||||
"downloadExamples": "下载示例图片",
|
||||
"clear": "清除选择",
|
||||
"skipMetadataRefreshCount": "跳过({count} 个模型)",
|
||||
"resumeMetadataRefreshCount": "恢复({count} 个模型)",
|
||||
"sendToWorkflow": "发送到工作流",
|
||||
"sections": {
|
||||
"workflow": "工作流",
|
||||
"metadata": "元数据",
|
||||
"attributes": "属性",
|
||||
"organize": "整理",
|
||||
"download": "下载"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "正在初始化自动整理...",
|
||||
"starting": "正在为 {type} 启动自动整理...",
|
||||
|
||||
@@ -692,9 +692,18 @@
|
||||
"unfavorite": "取消收藏",
|
||||
"deleteAll": "刪除所選",
|
||||
"downloadMissingLoras": "下載缺失的 LoRAs",
|
||||
"downloadExamples": "下載範例圖片",
|
||||
"clear": "清除選取",
|
||||
"skipMetadataRefreshCount": "跳過({count} 個模型)",
|
||||
"resumeMetadataRefreshCount": "恢復({count} 個模型)",
|
||||
"sendToWorkflow": "發送到工作流",
|
||||
"sections": {
|
||||
"workflow": "工作流",
|
||||
"metadata": "元數據",
|
||||
"attributes": "屬性",
|
||||
"organize": "整理",
|
||||
"download": "下載"
|
||||
},
|
||||
"autoOrganizeProgress": {
|
||||
"initializing": "正在初始化自動整理...",
|
||||
"starting": "正在開始自動整理 {type}...",
|
||||
|
||||
@@ -41,6 +41,63 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Section Headers */
|
||||
.context-menu-section-header {
|
||||
padding: 6px 12px 2px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-muted);
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Submenu */
|
||||
.context-menu-item.has-submenu {
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.submenu-arrow {
|
||||
margin-left: auto;
|
||||
font-size: 10px;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.context-submenu {
|
||||
position: absolute;
|
||||
left: calc(100% - 4px);
|
||||
top: -1px;
|
||||
display: none;
|
||||
background: var(--lora-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-xs);
|
||||
padding: 0;
|
||||
min-width: 200px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1001;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.context-submenu .context-menu-item {
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.context-submenu .context-menu-item:first-child {
|
||||
padding-top: 9px;
|
||||
}
|
||||
|
||||
.context-submenu .context-menu-item:last-child {
|
||||
padding-bottom: 9px;
|
||||
}
|
||||
|
||||
.context-submenu.flip-left {
|
||||
left: auto;
|
||||
right: 100%;
|
||||
}
|
||||
|
||||
/* NSFW Level Selector */
|
||||
.nsfw-level-selector {
|
||||
position: fixed;
|
||||
|
||||
@@ -3,32 +3,113 @@ export class BaseContextMenu {
|
||||
this.menu = document.getElementById(menuId);
|
||||
this.cardSelector = cardSelector;
|
||||
this.currentCard = null;
|
||||
|
||||
this.submenuTimeout = null;
|
||||
this.openSubmenu = null;
|
||||
|
||||
if (!this.menu) {
|
||||
console.error(`Context menu element with ID ${menuId} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Hide menu on regular clicks
|
||||
document.addEventListener('click', () => this.hideMenu());
|
||||
// Hide menu when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!this.menu.contains(e.target)) {
|
||||
this.hideMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle menu item clicks
|
||||
// Handle menu item clicks (including submenu items)
|
||||
this.menu.addEventListener('click', (e) => {
|
||||
const menuItem = e.target.closest('.context-menu-item');
|
||||
if (!menuItem || !this.currentCard) return;
|
||||
|
||||
// Ignore clicks on submenu trigger (has-submenu parent)
|
||||
if (menuItem.classList.contains('has-submenu')) return;
|
||||
|
||||
const action = menuItem.dataset.action;
|
||||
if (!action) return;
|
||||
|
||||
|
||||
this.handleMenuAction(action, menuItem);
|
||||
this.hideMenu();
|
||||
});
|
||||
|
||||
// Submenu hover handling
|
||||
// Use mouseover/mouseout (which bubble) with relatedTarget checks
|
||||
// to reliably detect crossing the .has-submenu boundary
|
||||
this.menu.addEventListener('mouseover', (e) => {
|
||||
const trigger = e.target.closest('.has-submenu');
|
||||
if (!trigger) return;
|
||||
|
||||
// Only act when entering from outside this trigger's tree
|
||||
if (e.relatedTarget && trigger.contains(e.relatedTarget)) return;
|
||||
|
||||
this._openSubmenu(trigger);
|
||||
});
|
||||
|
||||
this.menu.addEventListener('mouseout', (e) => {
|
||||
const trigger = e.target.closest('.has-submenu');
|
||||
if (!trigger) return;
|
||||
|
||||
// Only close when leaving the trigger's tree entirely
|
||||
if (e.relatedTarget && trigger.contains(e.relatedTarget)) return;
|
||||
|
||||
this._scheduleSubmenuClose(trigger);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
_openSubmenu(trigger) {
|
||||
// Clear any pending close
|
||||
if (this.submenuTimeout) {
|
||||
clearTimeout(this.submenuTimeout);
|
||||
this.submenuTimeout = null;
|
||||
}
|
||||
|
||||
// Hide any previously open submenu
|
||||
if (this.openSubmenu && this.openSubmenu !== trigger) {
|
||||
this._hideSubmenu(this.openSubmenu);
|
||||
}
|
||||
|
||||
const submenu = trigger.querySelector('.context-submenu');
|
||||
if (!submenu) return;
|
||||
|
||||
submenu.style.display = 'block';
|
||||
this.openSubmenu = trigger;
|
||||
this._positionSubmenu(submenu);
|
||||
}
|
||||
|
||||
_scheduleSubmenuClose(trigger) {
|
||||
this.submenuTimeout = setTimeout(() => {
|
||||
this._hideSubmenu(trigger);
|
||||
this.submenuTimeout = null;
|
||||
}, 250);
|
||||
}
|
||||
|
||||
_hideSubmenu(trigger) {
|
||||
const submenu = trigger.querySelector('.context-submenu');
|
||||
if (submenu) {
|
||||
submenu.style.display = 'none';
|
||||
submenu.classList.remove('flip-left');
|
||||
}
|
||||
if (this.openSubmenu === trigger) {
|
||||
this.openSubmenu = null;
|
||||
}
|
||||
}
|
||||
|
||||
_positionSubmenu(submenu) {
|
||||
const submenuRect = submenu.getBoundingClientRect();
|
||||
const viewportWidth = document.documentElement.clientWidth;
|
||||
|
||||
if (submenuRect.right > viewportWidth) {
|
||||
submenu.classList.add('flip-left');
|
||||
} else {
|
||||
submenu.classList.remove('flip-left');
|
||||
}
|
||||
}
|
||||
|
||||
handleMenuAction(action, menuItem) {
|
||||
// Override in subclass
|
||||
console.warn('handleMenuAction not implemented');
|
||||
@@ -40,34 +121,41 @@ export class BaseContextMenu {
|
||||
|
||||
// Get menu dimensions
|
||||
const menuRect = this.menu.getBoundingClientRect();
|
||||
|
||||
|
||||
// Get viewport dimensions
|
||||
const viewportWidth = document.documentElement.clientWidth;
|
||||
const viewportHeight = document.documentElement.clientHeight;
|
||||
|
||||
|
||||
// Calculate position
|
||||
let finalX = x;
|
||||
let finalY = y;
|
||||
|
||||
|
||||
// Ensure menu doesn't go offscreen right
|
||||
if (x + menuRect.width > viewportWidth) {
|
||||
finalX = x - menuRect.width;
|
||||
}
|
||||
|
||||
|
||||
// Ensure menu doesn't go offscreen bottom
|
||||
if (y + menuRect.height > viewportHeight) {
|
||||
finalY = y - menuRect.height;
|
||||
}
|
||||
|
||||
|
||||
// Position menu
|
||||
this.menu.style.left = `${finalX}px`;
|
||||
this.menu.style.top = `${finalY}px`;
|
||||
}
|
||||
|
||||
hideMenu() {
|
||||
if (this.submenuTimeout) {
|
||||
clearTimeout(this.submenuTimeout);
|
||||
this.submenuTimeout = null;
|
||||
}
|
||||
if (this.openSubmenu) {
|
||||
this._hideSubmenu(this.openSubmenu);
|
||||
}
|
||||
if (this.menu) {
|
||||
this.menu.style.display = 'none';
|
||||
}
|
||||
this.currentCard = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { bulkManager } from '../../managers/BulkManager.js';
|
||||
import { updateElementText, translate } from '../../utils/i18nHelpers.js';
|
||||
import { bulkMissingLoraDownloadManager } from '../../managers/BulkMissingLoraDownloadManager.js';
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||
|
||||
export class BulkContextMenu extends BaseContextMenu {
|
||||
constructor() {
|
||||
@@ -50,6 +51,14 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
if (copyAllItem) {
|
||||
copyAllItem.style.display = config.copyAll ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
// Submenu parent visibility
|
||||
const sendToWorkflowSubmenu = this.menu.querySelector('[data-has-submenu="send-to-workflow"]');
|
||||
if (sendToWorkflowSubmenu) {
|
||||
const hasWorkflowActions = config.sendToWorkflow || config.copyAll;
|
||||
sendToWorkflowSubmenu.style.display = hasWorkflowActions ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
if (refreshAllItem) {
|
||||
refreshAllItem.style.display = config.refreshAll ? 'flex' : 'none';
|
||||
}
|
||||
@@ -107,6 +116,13 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
downloadMissingLorasItem.style.display = currentModelType === 'recipes' ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
const downloadExampleImagesItem = this.menu.querySelector('[data-action="download-example-images"]');
|
||||
if (downloadExampleImagesItem) {
|
||||
// Show on model pages (loras, checkpoints, embeddings), hide on recipes
|
||||
const modelPages = ['loras', 'checkpoints', 'embeddings'];
|
||||
downloadExampleImagesItem.style.display = modelPages.includes(currentModelType) ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
const skipMetadataRefreshItem = this.menu.querySelector('[data-action="skip-metadata-refresh"]');
|
||||
const resumeMetadataRefreshItem = this.menu.querySelector('[data-action="resume-metadata-refresh"]');
|
||||
|
||||
@@ -140,6 +156,14 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide empty sections
|
||||
this.menu.querySelectorAll('.context-menu-section').forEach(section => {
|
||||
const items = Array.from(section.querySelectorAll('.context-menu-item'))
|
||||
.filter(item => !item.closest('.context-submenu'));
|
||||
const allHidden = items.length > 0 && items.every(item => item.style.display === 'none');
|
||||
section.style.display = allHidden ? 'none' : '';
|
||||
});
|
||||
}
|
||||
|
||||
updateSelectedCountHeader() {
|
||||
@@ -235,6 +259,9 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
case 'download-missing-loras':
|
||||
this.handleDownloadMissingLoras();
|
||||
break;
|
||||
case 'download-example-images':
|
||||
this.handleDownloadExampleImages();
|
||||
break;
|
||||
case 'clear':
|
||||
bulkManager.clearSelection();
|
||||
break;
|
||||
@@ -277,4 +304,31 @@ export class BulkContextMenu extends BaseContextMenu {
|
||||
|
||||
await bulkMissingLoraDownloadManager.downloadMissingLoras(selectedRecipes);
|
||||
}
|
||||
|
||||
async handleDownloadExampleImages() {
|
||||
if (state.selectedModels.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hashes = new Set();
|
||||
for (const filePath of state.selectedModels) {
|
||||
const escapedPath = CSS.escape(filePath);
|
||||
const card = document.querySelector(`.model-card[data-filepath="${escapedPath}"]`);
|
||||
if (card?.dataset?.sha256) {
|
||||
hashes.add(card.dataset.sha256);
|
||||
}
|
||||
}
|
||||
|
||||
if (hashes.size === 0) {
|
||||
showToast('No valid model hashes found in selection', {}, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const apiClient = getModelApiClient();
|
||||
await apiClient.downloadExampleImages([...hashes]);
|
||||
} catch (error) {
|
||||
console.error('Bulk download example images failed:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,49 +53,74 @@
|
||||
<span>{{ t('loras.bulkOperations.selected', {'count': 0}) }}</span>
|
||||
</div>
|
||||
<div class="context-menu-separator"></div>
|
||||
<div class="context-menu-item" data-action="refresh-all">
|
||||
<i class="fas fa-sync-alt"></i> <span>{{ t('loras.bulkOperations.refreshAll') }}</span>
|
||||
<div class="context-menu-section" data-section="workflow">
|
||||
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.workflow') }}</div>
|
||||
<div class="context-menu-item has-submenu" data-has-submenu="send-to-workflow">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
<span>{{ t('loras.bulkOperations.sendToWorkflow') }}</span>
|
||||
<i class="fas fa-chevron-right submenu-arrow"></i>
|
||||
<div class="context-submenu">
|
||||
<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>
|
||||
<div class="context-menu-item" data-action="send-to-workflow-replace">
|
||||
<i class="fas fa-exchange-alt"></i> <span>{{ t('loras.contextMenu.sendToWorkflowReplace') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="copy-all">
|
||||
<i class="fas fa-copy"></i> <span>{{ t('loras.bulkOperations.copyAll') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="check-updates">
|
||||
<i class="fas fa-bell"></i> <span>{{ t('loras.bulkOperations.checkUpdates') }}</span>
|
||||
<div class="context-menu-section" data-section="metadata">
|
||||
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.metadata') }}</div>
|
||||
<div class="context-menu-item" data-action="refresh-all">
|
||||
<i class="fas fa-sync-alt"></i> <span>{{ t('loras.bulkOperations.refreshAll') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="check-updates">
|
||||
<i class="fas fa-bell"></i> <span>{{ t('loras.bulkOperations.checkUpdates') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="skip-metadata-refresh">
|
||||
<i class="fas fa-ban"></i> <span>{{ t('loras.bulkOperations.skipMetadataRefresh') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="resume-metadata-refresh">
|
||||
<i class="fas fa-redo"></i> <span>{{ t('loras.bulkOperations.resumeMetadataRefresh') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="copy-all">
|
||||
<i class="fas fa-copy"></i> <span>{{ t('loras.bulkOperations.copyAll') }}</span>
|
||||
<div class="context-menu-section" data-section="attributes">
|
||||
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.attributes') }}</div>
|
||||
<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="set-favorite">
|
||||
<i class="fas fa-star"></i> <span>{{ t('loras.bulkOperations.setFavorite') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="set-content-rating">
|
||||
<i class="fas fa-exclamation-triangle"></i> <span>{{ t('loras.bulkOperations.setContentRating') }}</span>
|
||||
</div>
|
||||
</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 class="context-menu-section" data-section="organize">
|
||||
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.organize') }}</div>
|
||||
<div class="context-menu-item" data-action="auto-organize">
|
||||
<i class="fas fa-magic"></i> <span>{{ t('loras.bulkOperations.autoOrganize') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="move-all">
|
||||
<i class="fas fa-folder-open"></i> <span>{{ t('loras.bulkOperations.moveAll') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="send-to-workflow-replace">
|
||||
<i class="fas fa-exchange-alt"></i> <span>{{ t('loras.contextMenu.sendToWorkflowReplace') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="auto-organize">
|
||||
<i class="fas fa-magic"></i> <span>{{ t('loras.bulkOperations.autoOrganize') }}</span>
|
||||
</div>
|
||||
<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="set-favorite">
|
||||
<i class="fas fa-star"></i> <span>{{ t('loras.bulkOperations.setFavorite') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="set-content-rating">
|
||||
<i class="fas fa-exclamation-triangle"></i> <span>{{ t('loras.bulkOperations.setContentRating') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="skip-metadata-refresh">
|
||||
<i class="fas fa-ban"></i> <span>{{ t('loras.bulkOperations.skipMetadataRefresh') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="resume-metadata-refresh">
|
||||
<i class="fas fa-redo"></i> <span>{{ t('loras.bulkOperations.resumeMetadataRefresh') }}</span>
|
||||
<div class="context-menu-section" data-section="download">
|
||||
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.download') }}</div>
|
||||
<div class="context-menu-item" data-action="download-example-images">
|
||||
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadExamples') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="download-missing-loras">
|
||||
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingLoras') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="context-menu-separator"></div>
|
||||
<div class="context-menu-item" data-action="download-missing-loras">
|
||||
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingLoras') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="move-all">
|
||||
<i class="fas fa-folder-open"></i> <span>{{ t('loras.bulkOperations.moveAll') }}</span>
|
||||
</div>
|
||||
<div class="context-menu-item delete-item" data-action="delete-all">
|
||||
<i class="fas fa-trash"></i> <span>{{ t('loras.bulkOperations.deleteAll') }}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user