refactor(ui): group example image download actions into a submenu

Move the 'Download Missing' / 'Re-process All' example image actions
under a single 'Download Example Images' submenu item in the single-model
and bulk context menus, matching the existing send-to-workflow submenu
pattern. Shorten the submenu labels and update all locale translations.
This commit is contained in:
Will Miao
2026-08-03 21:17:42 +08:00
parent dc674098e7
commit 186ef4da78
15 changed files with 105 additions and 66 deletions

View File

@@ -772,8 +772,9 @@
"unfavorite": "Aus Favoriten entfernen",
"deleteAll": "Ausgewählte löschen",
"downloadMissingLoras": "Fehlende LoRAs herunterladen",
"downloadMissingExamples": "Fehlende Beispielbilder herunterladen",
"reprocessExamples": "Beispielbilder erneut verarbeiten",
"downloadExamples": "Beispielbilder herunterladen",
"downloadMissingExamples": "Fehlende herunterladen",
"reprocessExamples": "Alle erneut verarbeiten",
"clear": "Auswahl löschen",
"skipMetadataRefreshCount": "Überspringen{count} Modelle",
"resumeMetadataRefreshCount": "Fortsetzen{count} Modelle",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "An Workflow senden (Anhängen)",
"sendToWorkflowReplace": "An Workflow senden (Ersetzen)",
"openExamples": "Beispiele-Ordner öffnen",
"downloadExamples": "Fehlende Beispielbilder herunterladen",
"reprocessExamples": "Beispielbilder erneut verarbeiten",
"downloadExamples": "Beispielbilder herunterladen",
"downloadMissingExamples": "Fehlende herunterladen",
"reprocessExamples": "Alle erneut verarbeiten",
"replacePreview": "Vorschau ersetzen",
"setContentRating": "Inhaltsbewertung festlegen",
"moveToFolder": "In Ordner verschieben",

View File

@@ -772,8 +772,9 @@
"unfavorite": "Remove from Favorites",
"deleteAll": "Delete Selected",
"downloadMissingLoras": "Download Missing LoRAs",
"downloadMissingExamples": "Download Missing Example Images",
"reprocessExamples": "Re-process Example Images",
"downloadExamples": "Download Example Images",
"downloadMissingExamples": "Download Missing",
"reprocessExamples": "Re-process All",
"clear": "Clear Selection",
"skipMetadataRefreshCount": "Skip ({count} models)",
"resumeMetadataRefreshCount": "Resume ({count} models)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "Send to Workflow (Append)",
"sendToWorkflowReplace": "Send to Workflow (Replace)",
"openExamples": "Open Examples Folder",
"downloadExamples": "Download Missing Example Images",
"reprocessExamples": "Re-process Example Images",
"downloadExamples": "Download Example Images",
"downloadMissingExamples": "Download Missing",
"reprocessExamples": "Re-process All",
"replacePreview": "Replace Preview",
"setContentRating": "Set Content Rating",
"moveToFolder": "Move to Folder",

View File

@@ -772,8 +772,9 @@
"unfavorite": "Quitar de favoritos",
"deleteAll": "Eliminar seleccionados",
"downloadMissingLoras": "Descargar LoRAs faltantes",
"downloadMissingExamples": "Descargar imágenes de ejemplo faltantes",
"reprocessExamples": "Reprocesar imágenes de ejemplo",
"downloadExamples": "Descargar imágenes de ejemplo",
"downloadMissingExamples": "Descargar faltantes",
"reprocessExamples": "Reprocesar todo",
"clear": "Limpiar selección",
"skipMetadataRefreshCount": "Omitir{count} modelos",
"resumeMetadataRefreshCount": "Reanudar{count} modelos",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "Enviar al flujo de trabajo (Añadir)",
"sendToWorkflowReplace": "Enviar al flujo de trabajo (Reemplazar)",
"openExamples": "Abrir carpeta de ejemplos",
"downloadExamples": "Descargar imágenes de ejemplo faltantes",
"reprocessExamples": "Reprocesar imágenes de ejemplo",
"downloadExamples": "Descargar imágenes de ejemplo",
"downloadMissingExamples": "Descargar faltantes",
"reprocessExamples": "Reprocesar todo",
"replacePreview": "Reemplazar vista previa",
"setContentRating": "Establecer clasificación de contenido",
"moveToFolder": "Mover a carpeta",

View File

@@ -772,8 +772,9 @@
"unfavorite": "Retirer des favoris",
"deleteAll": "Supprimer la sélection",
"downloadMissingLoras": "Télécharger les LoRAs manquants",
"downloadMissingExamples": "Télécharger les images d'exemple manquantes",
"reprocessExamples": "Retraiter les images d'exemple",
"downloadExamples": "Télécharger les images d'exemple",
"downloadMissingExamples": "Télécharger les manquantes",
"reprocessExamples": "Tout retraiter",
"clear": "Effacer la sélection",
"skipMetadataRefreshCount": "Ignorer{count} modèles",
"resumeMetadataRefreshCount": "Reprendre{count} modèles",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "Envoyer vers le workflow (Ajouter)",
"sendToWorkflowReplace": "Envoyer vers le workflow (Remplacer)",
"openExamples": "Ouvrir le dossier d'exemples",
"downloadExamples": "Télécharger les images d'exemple manquantes",
"reprocessExamples": "Retraiter les images d'exemple",
"downloadExamples": "Télécharger les images d'exemple",
"downloadMissingExamples": "Télécharger les manquantes",
"reprocessExamples": "Tout retraiter",
"replacePreview": "Remplacer l'aperçu",
"setContentRating": "Définir la classification du contenu",
"moveToFolder": "Déplacer vers un dossier",

View File

@@ -772,8 +772,9 @@
"unfavorite": "הסר ממועדפים",
"deleteAll": "מחק נבחרים",
"downloadMissingLoras": "הורדת LoRAs חסרים",
"downloadMissingExamples": "הורדת תמונות דוגמה חסרות",
"reprocessExamples": "עיבוד מחדש של תמונות דוגמה",
"downloadExamples": "הורד תמונות דוגמה",
"downloadMissingExamples": "הורדת חסרים",
"reprocessExamples": "עיבוד מחדש של הכול",
"clear": "נקה בחירה",
"skipMetadataRefreshCount": "דילוג({count} מודלים)",
"resumeMetadataRefreshCount": "המשך({count} מודלים)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "שלח ל-Workflow (הוסף)",
"sendToWorkflowReplace": "שלח ל-Workflow (החלף)",
"openExamples": "פתח תיקיית דוגמאות",
"downloadExamples": "הורדת תמונות דוגמה חסרות",
"reprocessExamples": "עיבוד מחדש של תמונות דוגמה",
"downloadExamples": "הורד תמונות דוגמה",
"downloadMissingExamples": "הורדת חסרים",
"reprocessExamples": "עיבוד מחדש של הכול",
"replacePreview": "החלף תצוגה מקדימה",
"setContentRating": "הגדר דירוג תוכן",
"moveToFolder": "העבר לתיקייה",

View File

@@ -772,8 +772,9 @@
"unfavorite": "お気に入りから削除",
"deleteAll": "選択したものを削除",
"downloadMissingLoras": "不足している LoRA をダウンロード",
"downloadMissingExamples": "不足しているサンプル画像をダウンロード",
"reprocessExamples": "サンプル画像を再処理",
"downloadExamples": "画像をダウンロード",
"downloadMissingExamples": "不足分をダウンロード",
"reprocessExamples": "すべて再処理",
"clear": "選択をクリア",
"skipMetadataRefreshCount": "スキップ({count}モデル)",
"resumeMetadataRefreshCount": "再開({count}モデル)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "ワークフローに送信(追加)",
"sendToWorkflowReplace": "ワークフローに送信(置換)",
"openExamples": "例画像フォルダを開く",
"downloadExamples": "不足しているサンプル画像をダウンロード",
"reprocessExamples": "サンプル画像を再処理",
"downloadExamples": "画像をダウンロード",
"downloadMissingExamples": "不足分をダウンロード",
"reprocessExamples": "すべて再処理",
"replacePreview": "プレビューを置換",
"setContentRating": "コンテンツレーティングを設定",
"moveToFolder": "フォルダに移動",

View File

@@ -772,8 +772,9 @@
"unfavorite": "즐겨찾기 해제",
"deleteAll": "선택된 항목 삭제",
"downloadMissingLoras": "누락된 LoRA 다운로드",
"downloadMissingExamples": "누락된 예시 이미지 다운로드",
"reprocessExamples": "예시 이미지 다시 처리",
"downloadExamples": "예시 이미지 다운로드",
"downloadMissingExamples": "누락된 것만 다운로드",
"reprocessExamples": "모두 다시 처리",
"clear": "선택 지우기",
"skipMetadataRefreshCount": "건너뛰기({count}개 모델)",
"resumeMetadataRefreshCount": "재개({count}개 모델)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "워크플로로 전송 (추가)",
"sendToWorkflowReplace": "워크플로로 전송 (교체)",
"openExamples": "예시 폴더 열기",
"downloadExamples": "누락된 예시 이미지 다운로드",
"reprocessExamples": "예시 이미지 다시 처리",
"downloadExamples": "예시 이미지 다운로드",
"downloadMissingExamples": "누락된 것만 다운로드",
"reprocessExamples": "모두 다시 처리",
"replacePreview": "미리보기 교체",
"setContentRating": "콘텐츠 등급 설정",
"moveToFolder": "폴더로 이동",

View File

@@ -772,8 +772,9 @@
"unfavorite": "Удалить из избранного",
"deleteAll": "Удалить выбранные",
"downloadMissingLoras": "Скачать отсутствующие LoRAs",
"downloadMissingExamples": "Загрузить недостающие примеры изображений",
"reprocessExamples": "Повторно обработать примеры изображений",
"downloadExamples": "Загрузить примеры изображений",
"downloadMissingExamples": "Скачать недостающие",
"reprocessExamples": "Обработать всё заново",
"clear": "Очистить выбор",
"skipMetadataRefreshCount": "Пропустить({count} моделей)",
"resumeMetadataRefreshCount": "Возобновить({count} моделей)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "Отправить в Workflow (Добавить)",
"sendToWorkflowReplace": "Отправить в Workflow (Заменить)",
"openExamples": "Открыть папку примеров",
"downloadExamples": "Загрузить недостающие примеры изображений",
"reprocessExamples": "Повторно обработать примеры изображений",
"downloadExamples": "Загрузить примеры изображений",
"downloadMissingExamples": "Скачать недостающие",
"reprocessExamples": "Обработать всё заново",
"replacePreview": "Заменить превью",
"setContentRating": "Установить рейтинг контента",
"moveToFolder": "Переместить в папку",

View File

@@ -772,8 +772,9 @@
"unfavorite": "取消收藏",
"deleteAll": "删除已选",
"downloadMissingLoras": "下载缺失的 LoRAs",
"downloadMissingExamples": "下载缺失的示例图片",
"reprocessExamples": "重新处理示例图片",
"downloadExamples": "下载示例图片",
"downloadMissingExamples": "下载缺失的",
"reprocessExamples": "重新处理全部",
"clear": "清除选择",
"skipMetadataRefreshCount": "跳过({count} 个模型)",
"resumeMetadataRefreshCount": "恢复({count} 个模型)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "发送到工作流(追加)",
"sendToWorkflowReplace": "发送到工作流(替换)",
"openExamples": "打开示例文件夹",
"downloadExamples": "下载缺失的示例图片",
"reprocessExamples": "重新处理示例图片",
"downloadExamples": "下载示例图片",
"downloadMissingExamples": "下载缺失的",
"reprocessExamples": "重新处理全部",
"replacePreview": "替换预览",
"setContentRating": "设置内容评级",
"moveToFolder": "移动到文件夹",

View File

@@ -772,8 +772,9 @@
"unfavorite": "取消收藏",
"deleteAll": "刪除所選",
"downloadMissingLoras": "下載缺失的 LoRAs",
"downloadMissingExamples": "下載缺少的範例圖片",
"reprocessExamples": "重新處理範例圖片",
"downloadExamples": "下載範例圖片",
"downloadMissingExamples": "下載缺少的",
"reprocessExamples": "重新處理全部",
"clear": "清除選取",
"skipMetadataRefreshCount": "跳過({count} 個模型)",
"resumeMetadataRefreshCount": "恢復({count} 個模型)",
@@ -808,8 +809,9 @@
"sendToWorkflowAppend": "傳送到工作流(附加)",
"sendToWorkflowReplace": "傳送到工作流(取代)",
"openExamples": "開啟範例資料夾",
"downloadExamples": "下載缺少的範例圖片",
"reprocessExamples": "重新處理範例圖片",
"downloadExamples": "下載範例圖片",
"downloadMissingExamples": "下載缺少的",
"reprocessExamples": "重新處理全部",
"replacePreview": "更換預覽圖",
"setContentRating": "設定內容分級",
"moveToFolder": "移動到資料夾",

View File

@@ -137,17 +137,10 @@ export class BulkContextMenu extends BaseContextMenu {
downloadMissingLorasItem.style.display = currentModelType === 'recipes' ? 'flex' : 'none';
}
const downloadExampleImagesItem = this.menu.querySelector('[data-action="download-example-images"]');
if (downloadExampleImagesItem) {
const downloadExampleImagesSubmenu = this.menu.querySelector('[data-has-submenu="download-example-images"]');
if (downloadExampleImagesSubmenu) {
// Show on model pages (loras, checkpoints, embeddings), hide on recipes
const modelPages = ['loras', 'checkpoints', 'embeddings'];
downloadExampleImagesItem.style.display = modelPages.includes(currentModelType) ? 'flex' : 'none';
}
const downloadMissingExampleImagesItem = this.menu.querySelector('[data-action="download-missing-example-images"]');
if (downloadMissingExampleImagesItem) {
// Show on model pages (loras, checkpoints, embeddings), hide on recipes
downloadMissingExampleImagesItem.style.display = ['loras', 'checkpoints', 'embeddings'].includes(currentModelType) ? 'flex' : 'none';
downloadExampleImagesSubmenu.style.display = ['loras', 'checkpoints', 'embeddings'].includes(currentModelType) ? 'flex' : 'none';
}
const skipMetadataRefreshItem = this.menu.querySelector('[data-action="skip-metadata-refresh"]');

View File

@@ -32,8 +32,12 @@
<div class="context-menu-separator menu-section-break"></div>
<!-- Media / Preview -->
<div class="context-menu-item" data-action="preview"><i class="fas fa-folder-open"></i> {{ t('loras.contextMenu.openExamples') }}</div>
<div class="context-menu-item" data-action="download-examples"><i class="fas fa-download"></i> {{ t('loras.contextMenu.downloadExamples') }}</div>
<div class="context-menu-item" data-action="download-examples-force"><i class="fas fa-redo-alt"></i> {{ t('loras.contextMenu.reprocessExamples') }}</div>
<div class="context-menu-item has-submenu" data-has-submenu="download-examples"><i class="fas fa-download"></i> {{ t('loras.contextMenu.downloadExamples') }} <i class="fas fa-chevron-right submenu-arrow"></i>
<div class="context-submenu">
<div class="context-menu-item" data-action="download-examples"><i class="fas fa-download"></i> {{ t('loras.contextMenu.downloadMissingExamples') }}</div>
<div class="context-menu-item" data-action="download-examples-force"><i class="fas fa-redo-alt"></i> {{ t('loras.contextMenu.reprocessExamples') }}</div>
</div>
</div>
<div class="context-menu-item" data-action="replace-preview"><i class="fas fa-image"></i> {{ t('loras.contextMenu.replacePreview') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Attributes -->

View File

@@ -44,11 +44,18 @@
<div class="context-menu-item" data-action="preview">
<i class="fas fa-folder-open"></i> <span>{{ t('loras.contextMenu.openExamples') }}</span>
</div>
<div class="context-menu-item" data-action="download-examples">
<i class="fas fa-download"></i> <span>{{ t('loras.contextMenu.downloadExamples') }}</span>
</div>
<div class="context-menu-item" data-action="download-examples-force">
<i class="fas fa-redo-alt"></i> <span>{{ t('loras.contextMenu.reprocessExamples') }}</span>
<div class="context-menu-item has-submenu" data-has-submenu="download-examples">
<i class="fas fa-download"></i>
<span>{{ t('loras.contextMenu.downloadExamples') }}</span>
<i class="fas fa-chevron-right submenu-arrow"></i>
<div class="context-submenu">
<div class="context-menu-item" data-action="download-examples">
<i class="fas fa-download"></i> <span>{{ t('loras.contextMenu.downloadMissingExamples') }}</span>
</div>
<div class="context-menu-item" data-action="download-examples-force">
<i class="fas fa-redo-alt"></i> <span>{{ t('loras.contextMenu.reprocessExamples') }}</span>
</div>
</div>
</div>
<div class="context-menu-item" data-action="replace-preview">
<i class="fas fa-image"></i> <span>{{ t('loras.contextMenu.replacePreview') }}</span>
@@ -139,11 +146,18 @@
</div>
<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-missing-example-images">
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingExamples') }}</span>
</div>
<div class="context-menu-item" data-action="download-example-images">
<i class="fas fa-redo-alt"></i> <span>{{ t('loras.bulkOperations.reprocessExamples') }}</span>
<div class="context-menu-item has-submenu" data-has-submenu="download-example-images">
<i class="fas fa-download"></i>
<span>{{ t('loras.bulkOperations.downloadExamples') }}</span>
<i class="fas fa-chevron-right submenu-arrow"></i>
<div class="context-submenu">
<div class="context-menu-item" data-action="download-missing-example-images">
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingExamples') }}</span>
</div>
<div class="context-menu-item" data-action="download-example-images">
<i class="fas fa-redo-alt"></i> <span>{{ t('loras.bulkOperations.reprocessExamples') }}</span>
</div>
</div>
</div>
<div class="context-menu-item" data-action="download-missing-loras">
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingLoras') }}</span>

View File

@@ -32,8 +32,12 @@
<div class="context-menu-separator menu-section-break"></div>
<!-- Media / Preview -->
<div class="context-menu-item" data-action="preview"><i class="fas fa-folder-open"></i> {{ t('loras.contextMenu.openExamples') }}</div>
<div class="context-menu-item" data-action="download-examples"><i class="fas fa-download"></i> {{ t('loras.contextMenu.downloadExamples') }}</div>
<div class="context-menu-item" data-action="download-examples-force"><i class="fas fa-redo-alt"></i> {{ t('loras.contextMenu.reprocessExamples') }}</div>
<div class="context-menu-item has-submenu" data-has-submenu="download-examples"><i class="fas fa-download"></i> {{ t('loras.contextMenu.downloadExamples') }} <i class="fas fa-chevron-right submenu-arrow"></i>
<div class="context-submenu">
<div class="context-menu-item" data-action="download-examples"><i class="fas fa-download"></i> {{ t('loras.contextMenu.downloadMissingExamples') }}</div>
<div class="context-menu-item" data-action="download-examples-force"><i class="fas fa-redo-alt"></i> {{ t('loras.contextMenu.reprocessExamples') }}</div>
</div>
</div>
<div class="context-menu-item" data-action="replace-preview"><i class="fas fa-image"></i> {{ t('loras.contextMenu.replacePreview') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Attributes -->

View File

@@ -2159,8 +2159,12 @@ describe('Interaction-level regression coverage', () => {
it('routes single-model example downloads to missing-only and force paths', async () => {
document.body.innerHTML = `
<div id="loraContextMenu" class="context-menu">
<div class="context-menu-item" data-action="download-examples"></div>
<div class="context-menu-item" data-action="download-examples-force"></div>
<div class="context-menu-item has-submenu" data-has-submenu="download-examples">
<div class="context-submenu">
<div class="context-menu-item" data-action="download-examples"></div>
<div class="context-menu-item" data-action="download-examples-force"></div>
</div>
</div>
</div>
`;