mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-07-02 23:41:16 -03:00
fix(ui): unify HF file selection UI, remove cloud icon, add select-all, cleanup dead code (#965, #977)
- Unify single-URL and multi-URL HF repo flows to use the same batch preview interface (remove separate repoFileStep) - Remove unnecessary cloud icon from HF batch preview items - Use formatFileSize() instead of hardcoded MB text - Change default selection to unchecked (no preselected files) - Add select all / deselect all checkbox with dynamic Next button - Clean up dead CSS, HTML template, and JS methods from removed repoFileStep - Add selectAll i18n key with translations for all 10 locales - Fix batch progress bar name fallback for HF items
This commit is contained in:
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "Geben Sie eine CivitAI-, CivArchive- oder Hugging Face-URL pro Zeile ein. Unterstützt mehrere URLs für den Batch-Download.",
|
"urlHint": "Geben Sie eine CivitAI-, CivArchive- oder Hugging Face-URL pro Zeile ein. Unterstützt mehrere URLs für den Batch-Download.",
|
||||||
"selectHfFiles": "Datei(en) zum Herunterladen aus diesem Repository auswählen:",
|
"selectHfFiles": "Datei(en) zum Herunterladen aus diesem Repository auswählen:",
|
||||||
|
"selectAll": "Alle auswählen",
|
||||||
"fetchingRepoFiles": "Repository-Dateien werden abgerufen...",
|
"fetchingRepoFiles": "Repository-Dateien werden abgerufen...",
|
||||||
"locationPreview": "Download-Speicherort Vorschau",
|
"locationPreview": "Download-Speicherort Vorschau",
|
||||||
"useDefaultPath": "Standardpfad verwenden",
|
"useDefaultPath": "Standardpfad verwenden",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "Enter one CivitAI, CivArchive, or Hugging Face URL per line. Supports multiple URLs for batch download.",
|
"urlHint": "Enter one CivitAI, CivArchive, or Hugging Face URL per line. Supports multiple URLs for batch download.",
|
||||||
"selectHfFiles": "Select file(s) to download from this repository:",
|
"selectHfFiles": "Select file(s) to download from this repository:",
|
||||||
|
"selectAll": "Select All",
|
||||||
"fetchingRepoFiles": "Fetching repository files...",
|
"fetchingRepoFiles": "Fetching repository files...",
|
||||||
"locationPreview": "Download Location Preview",
|
"locationPreview": "Download Location Preview",
|
||||||
"useDefaultPath": "Use Default Path",
|
"useDefaultPath": "Use Default Path",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "Ingrese una URL de CivitAI, CivArchive o Hugging Face por línea. Admite múltiples URLs para descarga por lotes.",
|
"urlHint": "Ingrese una URL de CivitAI, CivArchive o Hugging Face por línea. Admite múltiples URLs para descarga por lotes.",
|
||||||
"selectHfFiles": "Seleccione el/los archivo(s) para descargar de este repositorio:",
|
"selectHfFiles": "Seleccione el/los archivo(s) para descargar de este repositorio:",
|
||||||
|
"selectAll": "Seleccionar todo",
|
||||||
"fetchingRepoFiles": "Obteniendo archivos del repositorio...",
|
"fetchingRepoFiles": "Obteniendo archivos del repositorio...",
|
||||||
"locationPreview": "Vista previa de ubicación de descarga",
|
"locationPreview": "Vista previa de ubicación de descarga",
|
||||||
"useDefaultPath": "Usar ruta predeterminada",
|
"useDefaultPath": "Usar ruta predeterminada",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "Entrez une URL CivitAI, CivArchive ou Hugging Face par ligne. Prend en charge plusieurs URL pour le téléchargement par lot.",
|
"urlHint": "Entrez une URL CivitAI, CivArchive ou Hugging Face par ligne. Prend en charge plusieurs URL pour le téléchargement par lot.",
|
||||||
"selectHfFiles": "Sélectionnez le(s) fichier(s) à télécharger depuis ce dépôt :",
|
"selectHfFiles": "Sélectionnez le(s) fichier(s) à télécharger depuis ce dépôt :",
|
||||||
|
"selectAll": "Tout sélectionner",
|
||||||
"fetchingRepoFiles": "Récupération des fichiers du dépôt...",
|
"fetchingRepoFiles": "Récupération des fichiers du dépôt...",
|
||||||
"locationPreview": "Aperçu de l'emplacement de téléchargement",
|
"locationPreview": "Aperçu de l'emplacement de téléchargement",
|
||||||
"useDefaultPath": "Utiliser le chemin par défaut",
|
"useDefaultPath": "Utiliser le chemin par défaut",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "יש להזין כתובת URL אחת של CivitAI, CivArchive או Hugging Face בכל שורה. תומך במספר כתובות URL להורדה בקבוצה.",
|
"urlHint": "יש להזין כתובת URL אחת של CivitAI, CivArchive או Hugging Face בכל שורה. תומך במספר כתובות URL להורדה בקבוצה.",
|
||||||
"selectHfFiles": "בחר קבצים להורדה ממאגר זה:",
|
"selectHfFiles": "בחר קבצים להורדה ממאגר זה:",
|
||||||
|
"selectAll": "בחר הכל",
|
||||||
"fetchingRepoFiles": "מביא קבצים מהמאגר...",
|
"fetchingRepoFiles": "מביא קבצים מהמאגר...",
|
||||||
"locationPreview": "תצוגה מקדימה של מיקום ההורדה",
|
"locationPreview": "תצוגה מקדימה של מיקום ההורדה",
|
||||||
"useDefaultPath": "השתמש בנתיב ברירת מחדל",
|
"useDefaultPath": "השתמש בנתיב ברירת מחדל",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "1行に1つのCivitAI、CivArchive、またはHugging Face URLを入力してください。複数のURLを一括ダウンロードできます。",
|
"urlHint": "1行に1つのCivitAI、CivArchive、またはHugging Face URLを入力してください。複数のURLを一括ダウンロードできます。",
|
||||||
"selectHfFiles": "このリポジトリからダウンロードするファイルを選択してください:",
|
"selectHfFiles": "このリポジトリからダウンロードするファイルを選択してください:",
|
||||||
|
"selectAll": "すべて選択",
|
||||||
"fetchingRepoFiles": "リポジトリのファイルを取得中...",
|
"fetchingRepoFiles": "リポジトリのファイルを取得中...",
|
||||||
"locationPreview": "ダウンロード場所プレビュー",
|
"locationPreview": "ダウンロード場所プレビュー",
|
||||||
"useDefaultPath": "デフォルトパスを使用",
|
"useDefaultPath": "デフォルトパスを使用",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "한 줄에 하나의 CivitAI, CivArchive 또는 Hugging Face URL을 입력하세요. 여러 URL을 일괄 다운로드할 수 있습니다.",
|
"urlHint": "한 줄에 하나의 CivitAI, CivArchive 또는 Hugging Face URL을 입력하세요. 여러 URL을 일괄 다운로드할 수 있습니다.",
|
||||||
"selectHfFiles": "이 저장소에서 다운로드할 파일을 선택하세요:",
|
"selectHfFiles": "이 저장소에서 다운로드할 파일을 선택하세요:",
|
||||||
|
"selectAll": "모두 선택",
|
||||||
"fetchingRepoFiles": "저장소 파일을 가져오는 중...",
|
"fetchingRepoFiles": "저장소 파일을 가져오는 중...",
|
||||||
"locationPreview": "다운로드 위치 미리보기",
|
"locationPreview": "다운로드 위치 미리보기",
|
||||||
"useDefaultPath": "기본 경로 사용",
|
"useDefaultPath": "기본 경로 사용",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "Введите один URL CivitAI, CivArchive или Hugging Face в каждой строке. Поддерживает несколько URL для пакетной загрузки.",
|
"urlHint": "Введите один URL CivitAI, CivArchive или Hugging Face в каждой строке. Поддерживает несколько URL для пакетной загрузки.",
|
||||||
"selectHfFiles": "Выберите файл(ы) для загрузки из этого репозитория:",
|
"selectHfFiles": "Выберите файл(ы) для загрузки из этого репозитория:",
|
||||||
|
"selectAll": "Выбрать все",
|
||||||
"fetchingRepoFiles": "Получение файлов репозитория...",
|
"fetchingRepoFiles": "Получение файлов репозитория...",
|
||||||
"locationPreview": "Предпросмотр места загрузки",
|
"locationPreview": "Предпросмотр места загрузки",
|
||||||
"useDefaultPath": "Использовать путь по умолчанию",
|
"useDefaultPath": "Использовать путь по умолчанию",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "每行输入一个 CivitAI、CivArchive 或 Hugging Face URL。支持批量下载多个 URL。",
|
"urlHint": "每行输入一个 CivitAI、CivArchive 或 Hugging Face URL。支持批量下载多个 URL。",
|
||||||
"selectHfFiles": "选择从此仓库下载的文件:",
|
"selectHfFiles": "选择从此仓库下载的文件:",
|
||||||
|
"selectAll": "全选",
|
||||||
"fetchingRepoFiles": "正在获取仓库文件...",
|
"fetchingRepoFiles": "正在获取仓库文件...",
|
||||||
"locationPreview": "下载位置预览",
|
"locationPreview": "下载位置预览",
|
||||||
"useDefaultPath": "使用默认路径",
|
"useDefaultPath": "使用默认路径",
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@
|
|||||||
"placeholder": "https://civitai.com/models/...",
|
"placeholder": "https://civitai.com/models/...",
|
||||||
"urlHint": "每行輸入一個 CivitAI、CivArchive 或 Hugging Face URL。支援批量下載多個 URL。",
|
"urlHint": "每行輸入一個 CivitAI、CivArchive 或 Hugging Face URL。支援批量下載多個 URL。",
|
||||||
"selectHfFiles": "選擇從此倉庫下載的檔案:",
|
"selectHfFiles": "選擇從此倉庫下載的檔案:",
|
||||||
|
"selectAll": "全選",
|
||||||
"fetchingRepoFiles": "正在獲取倉庫檔案...",
|
"fetchingRepoFiles": "正在獲取倉庫檔案...",
|
||||||
"locationPreview": "下載位置預覽",
|
"locationPreview": "下載位置預覽",
|
||||||
"useDefaultPath": "使用預設路徑",
|
"useDefaultPath": "使用預設路徑",
|
||||||
|
|||||||
@@ -823,87 +823,6 @@
|
|||||||
background: var(--lora-surface);
|
background: var(--lora-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HF Repo File Explorer Step */
|
|
||||||
.hf-repo-header {
|
|
||||||
margin-bottom: var(--space-2);
|
|
||||||
font-size: 0.95em;
|
|
||||||
color: var(--text-color);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-list {
|
|
||||||
max-height: 360px;
|
|
||||||
overflow-y: auto;
|
|
||||||
margin: var(--space-2) 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: var(--border-radius-sm);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: var(--transition-base);
|
|
||||||
background: var(--bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-item:hover {
|
|
||||||
border-color: var(--lora-accent);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-item.selected {
|
|
||||||
border: 2px solid var(--lora-accent);
|
|
||||||
background: oklch(var(--lora-accent) / 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-item .repo-file-checkbox {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
cursor: pointer;
|
|
||||||
accent-color: var(--lora-accent);
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
color: var(--text-color);
|
|
||||||
opacity: 0.6;
|
|
||||||
width: 24px;
|
|
||||||
text-align: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-name {
|
|
||||||
flex: 1;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.95em;
|
|
||||||
word-break: keep-all;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-meta {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: var(--text-color);
|
|
||||||
opacity: 0.6;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-file-size {
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hf-badge {
|
.hf-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 1px 6px;
|
padding: 1px 6px;
|
||||||
@@ -915,9 +834,6 @@
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .repo-file-item {
|
|
||||||
background: var(--lora-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checkbox inside HF batch preview items */
|
/* Checkbox inside HF batch preview items */
|
||||||
.batch-preview-checkbox {
|
.batch-preview-checkbox {
|
||||||
@@ -929,4 +845,42 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Select All toolbar in batch preview */
|
||||||
|
.batch-preview-select-all {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
background: var(--lora-surface);
|
||||||
|
cursor: pointer;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-select-all input[type="checkbox"] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
accent-color: var(--lora-accent);
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-select-all label {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .batch-preview-select-all {
|
||||||
|
background: var(--lora-surface);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
|||||||
import { FolderTreeManager } from '../components/FolderTreeManager.js';
|
import { FolderTreeManager } from '../components/FolderTreeManager.js';
|
||||||
import { translate } from '../utils/i18nHelpers.js';
|
import { translate } from '../utils/i18nHelpers.js';
|
||||||
import { extractCivitaiModelUrlParts } from '../utils/civitaiUtils.js';
|
import { extractCivitaiModelUrlParts } from '../utils/civitaiUtils.js';
|
||||||
|
import { formatFileSize } from '../utils/formatters.js';
|
||||||
|
|
||||||
export class DownloadManager {
|
export class DownloadManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -29,7 +30,6 @@ export class DownloadManager {
|
|||||||
|
|
||||||
// HF download state
|
// HF download state
|
||||||
this.hfRepoId = null;
|
this.hfRepoId = null;
|
||||||
this.hfRepoFiles = [];
|
|
||||||
this.hfSelectedFiles = [];
|
this.hfSelectedFiles = [];
|
||||||
|
|
||||||
this.loadingManager = new LoadingManager();
|
this.loadingManager = new LoadingManager();
|
||||||
@@ -50,9 +50,7 @@ export class DownloadManager {
|
|||||||
this.handleBackToUrlFromBatch = this.backToUrlFromBatch.bind(this);
|
this.handleBackToUrlFromBatch = this.backToUrlFromBatch.bind(this);
|
||||||
this.handleNextFromBatch = this.nextFromBatch.bind(this);
|
this.handleNextFromBatch = this.nextFromBatch.bind(this);
|
||||||
|
|
||||||
// HF handlers
|
|
||||||
this.handleBackToUrlFromHf = this.backToUrlFromHf.bind(this);
|
|
||||||
this.handleNextFromHfFiles = this.nextFromHfFiles.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showDownloadModal() {
|
showDownloadModal() {
|
||||||
@@ -109,11 +107,7 @@ export class DownloadManager {
|
|||||||
// Default path toggle handler
|
// Default path toggle handler
|
||||||
document.getElementById('useDefaultPath').addEventListener('change', this.handleToggleDefaultPath);
|
document.getElementById('useDefaultPath').addEventListener('change', this.handleToggleDefaultPath);
|
||||||
|
|
||||||
// HF step buttons
|
|
||||||
const backToUrlFromHfBtn = document.getElementById('backToUrlFromHfBtn');
|
|
||||||
if (backToUrlFromHfBtn) backToUrlFromHfBtn.addEventListener('click', this.handleBackToUrlFromHf);
|
|
||||||
const nextFromHfFiles = document.getElementById('nextFromHfFiles');
|
|
||||||
if (nextFromHfFiles) nextFromHfFiles.addEventListener('click', this.handleNextFromHfFiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateModalLabels() {
|
updateModalLabels() {
|
||||||
@@ -178,7 +172,6 @@ export class DownloadManager {
|
|||||||
|
|
||||||
// Reset HF state
|
// Reset HF state
|
||||||
this.hfRepoId = null;
|
this.hfRepoId = null;
|
||||||
this.hfRepoFiles = [];
|
|
||||||
this.hfSelectedFiles = [];
|
this.hfSelectedFiles = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,24 +317,36 @@ export class DownloadManager {
|
|||||||
this.isBatchMode = false;
|
this.isBatchMode = false;
|
||||||
this.hfRepoId = info.repo;
|
this.hfRepoId = info.repo;
|
||||||
this.hfSelectedFiles = [info.filename];
|
this.hfSelectedFiles = [info.filename];
|
||||||
this.hfRepoFiles = [];
|
|
||||||
this.source = 'huggingface';
|
this.source = 'huggingface';
|
||||||
this.proceedToLocation();
|
this.proceedToLocation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Repo URL → fetch file list
|
// Repo URL → fetch file list and convert to batch items
|
||||||
try {
|
try {
|
||||||
this.loadingManager.showSimpleLoading(translate('modals.download.fetchingRepoFiles'));
|
this.loadingManager.showSimpleLoading(translate('modals.download.fetchingRepoFiles'));
|
||||||
const files = await this.apiClient.fetchHfRepoFiles(info.repo);
|
const files = await this.apiClient.fetchHfRepoFiles(info.repo);
|
||||||
if (!files || files.length === 0) {
|
if (!files || files.length === 0) {
|
||||||
throw new Error(translate('modals.download.errors.noModelFiles'));
|
throw new Error(translate('modals.download.errors.noModelFiles'));
|
||||||
}
|
}
|
||||||
this.hfRepoId = info.repo;
|
this.isBatchMode = true;
|
||||||
this.hfRepoFiles = files;
|
this.batchModels = [];
|
||||||
this.hfSelectedFiles = [];
|
|
||||||
this.isBatchMode = false;
|
|
||||||
this.source = 'huggingface';
|
this.source = 'huggingface';
|
||||||
this.showRepoFileStep(info.repo);
|
for (const file of files) {
|
||||||
|
this.batchModels.push({
|
||||||
|
url: urls[0],
|
||||||
|
source: 'huggingface',
|
||||||
|
repo: info.repo,
|
||||||
|
filename: file.filename,
|
||||||
|
revision: 'main',
|
||||||
|
displayName: file.filename,
|
||||||
|
fileSizeBytes: file.size,
|
||||||
|
selectedVersion: true,
|
||||||
|
versions: [],
|
||||||
|
checked: false,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.showBatchPreviewStep();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorElement.textContent = err.message;
|
errorElement.textContent = err.message;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -372,7 +377,7 @@ export class DownloadManager {
|
|||||||
displayName: info.filename,
|
displayName: info.filename,
|
||||||
selectedVersion: true,
|
selectedVersion: true,
|
||||||
versions: [],
|
versions: [],
|
||||||
checked: true,
|
checked: false,
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
} else if (info.type === 'hf-repo') {
|
} else if (info.type === 'hf-repo') {
|
||||||
@@ -394,7 +399,7 @@ export class DownloadManager {
|
|||||||
fileSizeBytes: file.size,
|
fileSizeBytes: file.size,
|
||||||
selectedVersion: true,
|
selectedVersion: true,
|
||||||
versions: [],
|
versions: [],
|
||||||
checked: true,
|
checked: false,
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -408,48 +413,6 @@ export class DownloadManager {
|
|||||||
this.showBatchPreviewStep();
|
this.showBatchPreviewStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
showRepoFileStep(repoId) {
|
|
||||||
document.querySelectorAll('.download-step').forEach(s => s.style.display = 'none');
|
|
||||||
document.getElementById('repoFileStep').style.display = 'block';
|
|
||||||
document.getElementById('hfRepoLabel').textContent = repoId;
|
|
||||||
|
|
||||||
const list = document.getElementById('repoFileList');
|
|
||||||
list.innerHTML = this.hfRepoFiles.map((f, i) => {
|
|
||||||
const sizeMb = f.size > 0 ? (f.size / (1024 * 1024)).toFixed(1) : '?';
|
|
||||||
return `
|
|
||||||
<div class="repo-file-item" data-index="${i}">
|
|
||||||
<input type="checkbox" class="repo-file-checkbox" data-index="${i}" />
|
|
||||||
<span class="repo-file-icon"><i class="fas fa-file"></i></span>
|
|
||||||
<span class="repo-file-name">${f.filename}</span>
|
|
||||||
<span class="repo-file-meta">
|
|
||||||
<span class="repo-file-size">${sizeMb} MB</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
backToUrlFromHf() {
|
|
||||||
this.hfRepoId = null;
|
|
||||||
this.hfRepoFiles = [];
|
|
||||||
this.hfSelectedFiles = [];
|
|
||||||
document.getElementById('repoFileStep').style.display = 'none';
|
|
||||||
document.getElementById('urlStep').style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
nextFromHfFiles() {
|
|
||||||
// Read checked state directly from DOM — more reliable than event-tracking
|
|
||||||
const checked = document.querySelectorAll('.repo-file-checkbox:checked');
|
|
||||||
this.hfSelectedFiles = Array.from(checked).map(cb => {
|
|
||||||
const idx = parseInt(cb.dataset.index);
|
|
||||||
return this.hfRepoFiles[idx].filename;
|
|
||||||
});
|
|
||||||
if (!this.hfSelectedFiles.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.proceedToLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchVersionsForCurrentModel() {
|
async fetchVersionsForCurrentModel() {
|
||||||
const errorElement = document.getElementById('urlError');
|
const errorElement = document.getElementById('urlError');
|
||||||
if (errorElement) {
|
if (errorElement) {
|
||||||
@@ -1114,7 +1077,9 @@ export class DownloadManager {
|
|||||||
` (${validCount})`;
|
` (${validCount})`;
|
||||||
|
|
||||||
const list = document.getElementById('batchPreviewList');
|
const list = document.getElementById('batchPreviewList');
|
||||||
list.innerHTML = this.batchModels.map((item, index) => {
|
const hasHfItems = this.batchModels.some(m => m.source === 'huggingface' && !m.error);
|
||||||
|
|
||||||
|
let itemsHtml = this.batchModels.map((item, index) => {
|
||||||
if (item.error) {
|
if (item.error) {
|
||||||
return `
|
return `
|
||||||
<div class="batch-preview-item batch-preview-error" data-index="${index}">
|
<div class="batch-preview-item batch-preview-error" data-index="${index}">
|
||||||
@@ -1137,19 +1102,16 @@ export class DownloadManager {
|
|||||||
// HF batch item rendering with checkbox
|
// HF batch item rendering with checkbox
|
||||||
if (item.source === 'huggingface') {
|
if (item.source === 'huggingface') {
|
||||||
const hfSize = item.fileSizeBytes
|
const hfSize = item.fileSizeBytes
|
||||||
? (item.fileSizeBytes / (1024 * 1024)).toFixed(1)
|
? formatFileSize(item.fileSizeBytes)
|
||||||
: '?';
|
: '?';
|
||||||
return `
|
return `
|
||||||
<div class="batch-preview-item" data-index="${index}">
|
<div class="batch-preview-item" data-index="${index}">
|
||||||
<input type="checkbox" class="batch-preview-checkbox"
|
<input type="checkbox" class="batch-preview-checkbox"
|
||||||
data-index="${index}" ${item.checked !== false ? 'checked' : ''} />
|
data-index="${index}" ${item.checked !== false ? 'checked' : ''} />
|
||||||
<div class="batch-preview-icon" style="color: var(--lora-accent);">
|
|
||||||
<i class="fas fa-cloud"></i>
|
|
||||||
</div>
|
|
||||||
<div class="batch-preview-info">
|
<div class="batch-preview-info">
|
||||||
<div class="batch-preview-name">${item.displayName || item.filename || `HF #${index}`} <span class="hf-badge">HF</span></div>
|
<div class="batch-preview-name">${item.displayName || item.filename || `HF #${index}`} <span class="hf-badge">HF</span></div>
|
||||||
<div class="batch-preview-meta">
|
<div class="batch-preview-meta">
|
||||||
<span>${hfSize} MB</span>
|
<span>${hfSize}</span>
|
||||||
<span>${item.repo || ''}</span>
|
<span>${item.repo || ''}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1189,6 +1151,21 @@ export class DownloadManager {
|
|||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
// Prepend select-all toolbar if there are HF items with checkboxes
|
||||||
|
if (hasHfItems) {
|
||||||
|
const allChecked = this.batchModels
|
||||||
|
.filter(m => m.source === 'huggingface' && !m.error)
|
||||||
|
.every(m => m.checked !== false);
|
||||||
|
itemsHtml = `
|
||||||
|
<div class="batch-preview-select-all">
|
||||||
|
<input type="checkbox" id="batchSelectAll" ${allChecked ? 'checked' : ''} />
|
||||||
|
<label for="batchSelectAll">${translate('modals.download.selectAll', {}, 'Select All')}</label>
|
||||||
|
</div>
|
||||||
|
` + itemsHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.innerHTML = itemsHtml;
|
||||||
|
|
||||||
list.onclick = (e) => {
|
list.onclick = (e) => {
|
||||||
const removeBtn = e.target.closest('.batch-preview-remove');
|
const removeBtn = e.target.closest('.batch-preview-remove');
|
||||||
if (removeBtn) {
|
if (removeBtn) {
|
||||||
@@ -1212,16 +1189,51 @@ export class DownloadManager {
|
|||||||
if (this.batchModels[idx]) {
|
if (this.batchModels[idx]) {
|
||||||
this.batchModels[idx].checked = e.target.checked;
|
this.batchModels[idx].checked = e.target.checked;
|
||||||
}
|
}
|
||||||
// Update valid count in title
|
// Update valid count in title and Next button
|
||||||
const checkedCount = this.batchModels.filter(
|
const checkedCount = this.batchModels.filter(
|
||||||
m => !m.error && m.checked !== false
|
m => !m.error && m.checked !== false
|
||||||
).length;
|
).length;
|
||||||
document.getElementById('downloadModalTitle').textContent =
|
document.getElementById('downloadModalTitle').textContent =
|
||||||
translate('modals.download.titleWithType', { type: this.apiClient.apiConfig.config.displayName }) +
|
translate('modals.download.titleWithType', { type: this.apiClient.apiConfig.config.displayName }) +
|
||||||
` (${checkedCount})`;
|
` (${checkedCount})`;
|
||||||
|
const nextBtn = document.getElementById('nextFromBatchBtn');
|
||||||
|
nextBtn.disabled = checkedCount === 0;
|
||||||
|
nextBtn.classList.toggle('disabled', checkedCount === 0);
|
||||||
|
// Update select-all checkbox state
|
||||||
|
const selectAll = document.getElementById('batchSelectAll');
|
||||||
|
if (selectAll) {
|
||||||
|
const hfItems = this.batchModels.filter(m => m.source === 'huggingface' && !m.error);
|
||||||
|
selectAll.checked = hfItems.length > 0 && hfItems.every(m => m.checked !== false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Select-all handler
|
||||||
|
const selectAll = document.getElementById('batchSelectAll');
|
||||||
|
if (selectAll) {
|
||||||
|
selectAll.addEventListener('change', (e) => {
|
||||||
|
const checked = e.target.checked;
|
||||||
|
const hfCheckboxes = list.querySelectorAll('.batch-preview-checkbox');
|
||||||
|
hfCheckboxes.forEach(cb => {
|
||||||
|
cb.checked = checked;
|
||||||
|
const idx = parseInt(cb.dataset.index);
|
||||||
|
if (this.batchModels[idx]) {
|
||||||
|
this.batchModels[idx].checked = checked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Update valid count in title and Next button
|
||||||
|
const checkedCount = this.batchModels.filter(
|
||||||
|
m => !m.error && m.checked !== false
|
||||||
|
).length;
|
||||||
|
document.getElementById('downloadModalTitle').textContent =
|
||||||
|
translate('modals.download.titleWithType', { type: this.apiClient.apiConfig.config.displayName }) +
|
||||||
|
` (${checkedCount})`;
|
||||||
|
const nextBtn = document.getElementById('nextFromBatchBtn');
|
||||||
|
nextBtn.disabled = checkedCount === 0;
|
||||||
|
nextBtn.classList.toggle('disabled', checkedCount === 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const nextBtn = document.getElementById('nextFromBatchBtn');
|
const nextBtn = document.getElementById('nextFromBatchBtn');
|
||||||
nextBtn.disabled = validCount === 0;
|
nextBtn.disabled = validCount === 0;
|
||||||
nextBtn.classList.toggle('disabled', validCount === 0);
|
nextBtn.classList.toggle('disabled', validCount === 0);
|
||||||
@@ -1360,7 +1372,7 @@ export class DownloadManager {
|
|||||||
|
|
||||||
if (data.status === 'progress' && data.download_id?.startsWith(batchDownloadId)) {
|
if (data.status === 'progress' && data.download_id?.startsWith(batchDownloadId)) {
|
||||||
const current = downloadItems[completedDownloads + failedDownloads];
|
const current = downloadItems[completedDownloads + failedDownloads];
|
||||||
const name = current?.selectedVersion?.name || `#${completedDownloads + failedDownloads + 1}`;
|
const name = current?.selectedVersion?.name || current?.displayName || current?.filename || `#${completedDownloads + failedDownloads + 1}`;
|
||||||
const metrics = {
|
const metrics = {
|
||||||
bytesDownloaded: data.bytes_downloaded,
|
bytesDownloaded: data.bytes_downloaded,
|
||||||
totalBytes: data.total_bytes,
|
totalBytes: data.total_bytes,
|
||||||
|
|||||||
@@ -22,24 +22,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Step 1b: HF Repo File Explorer (shown when HF repo URL is detected) -->
|
|
||||||
<div class="download-step" id="repoFileStep" style="display: none;">
|
|
||||||
<div class="input-group">
|
|
||||||
<label>{{ t('modals.download.selectHfFiles') }}</label>
|
|
||||||
<div class="hf-repo-header">
|
|
||||||
<span id="hfRepoLabel" class="hf-repo-label"></span>
|
|
||||||
</div>
|
|
||||||
<div class="repo-file-list" id="repoFileList">
|
|
||||||
<!-- Files will be inserted here dynamically -->
|
|
||||||
</div>
|
|
||||||
<div class="error-message" id="repoFileError"></div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-actions">
|
|
||||||
<button class="secondary-btn" id="backToUrlFromHfBtn">{{ t('common.actions.back') }}</button>
|
|
||||||
<button class="primary-btn" id="nextFromHfFiles">{{ t('common.actions.next') }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Step 2: Batch Preview (multi-URL mode) -->
|
<!-- Step 2: Batch Preview (multi-URL mode) -->
|
||||||
<div class="download-step" id="batchPreviewStep" style="display: none;">
|
<div class="download-step" id="batchPreviewStep" style="display: none;">
|
||||||
<div class="batch-preview-list" id="batchPreviewList">
|
<div class="batch-preview-list" id="batchPreviewList">
|
||||||
|
|||||||
Reference in New Issue
Block a user