mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-07-13 20:21:16 -03:00
feat(ui): group HF batch files by repo with collapse/expand, fix nested scroll & collapse animation
- Group HF batch download files by repo with collapsible group headers - Fix nested scrollbar conflict (inner scrollbar undraggable) by making batch-preview-list flex-fill - Fix collapse animation glitch (items disappearing before container shrinks) by keeping expanded during max-height transition - Visual polish: hover lift, backdrop-filter glass, design token alignment - Remove redundant database icon from group header - Guard transitionend handlers against rapid-click races
This commit is contained in:
@@ -577,13 +577,14 @@
|
|||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: var(--transition-base);
|
transition: var(--transition-base), box-shadow var(--transition-fast), transform var(--transition-fast);
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-option:hover {
|
.file-option:hover {
|
||||||
border-color: var(--lora-accent);
|
border-color: var(--lora-accent);
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-md);
|
||||||
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-option.selected {
|
.file-option.selected {
|
||||||
@@ -698,10 +699,25 @@
|
|||||||
color: var(--lora-accent);
|
color: var(--lora-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Batch Preview List */
|
/* BUG 1 FIX: Single scrollbar — modal-content becomes a flex column so the
|
||||||
|
batch preview step can flex; the list scrolls instead of the modal-content. */
|
||||||
|
#downloadModal .modal-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#batchPreviewStep {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Batch Preview List — no max-height; flexes inside #batchPreviewStep */
|
||||||
.batch-preview-list {
|
.batch-preview-list {
|
||||||
max-height: 400px;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
margin: var(--space-2) 0;
|
margin: var(--space-2) 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -859,6 +875,8 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.batch-preview-select-all input[type="checkbox"] {
|
.batch-preview-select-all input[type="checkbox"] {
|
||||||
@@ -884,3 +902,100 @@
|
|||||||
[data-theme="dark"] .batch-preview-select-all {
|
[data-theme="dark"] .batch-preview-select-all {
|
||||||
background: var(--lora-surface);
|
background: var(--lora-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FEATURE 2: HF repo grouping — collapsible groups by repo */
|
||||||
|
.batch-preview-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--surface-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: var(--color-accent-subtle);
|
||||||
|
border-bottom: 1px solid var(--color-accent-border);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
transition: background var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-header:hover {
|
||||||
|
background: oklch(var(--color-accent-l) var(--color-accent-c) var(--color-accent-h) / 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-toggle {
|
||||||
|
width: 14px;
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: transform var(--transition-fast);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-toggle.expanded {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-name {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-count {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.7;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-select-all {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
accent-color: var(--lora-accent);
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
background: var(--border-color);
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: max-height 0.35s ease, opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-preview-group-body.expanded {
|
||||||
|
opacity: 1;
|
||||||
|
max-height: 9999px; /* rest state: content visible; JS inline style overrides during transitions */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme overrides for group styles */
|
||||||
|
[data-theme="dark"] .batch-preview-group {
|
||||||
|
background: var(--surface-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .batch-preview-group-header {
|
||||||
|
background: var(--color-accent-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .batch-preview-group-header:hover {
|
||||||
|
background: oklch(var(--color-accent-l) var(--color-accent-c) var(--color-accent-h) / 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .batch-preview-group-body {
|
||||||
|
background: var(--border-color);
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export class DownloadManager {
|
|||||||
// HF download state
|
// HF download state
|
||||||
this.hfRepoId = null;
|
this.hfRepoId = null;
|
||||||
this.hfSelectedFiles = [];
|
this.hfSelectedFiles = [];
|
||||||
|
this.hfRepoCollapsed = {};
|
||||||
|
|
||||||
this.loadingManager = new LoadingManager();
|
this.loadingManager = new LoadingManager();
|
||||||
this.folderTreeManager = new FolderTreeManager();
|
this.folderTreeManager = new FolderTreeManager();
|
||||||
@@ -174,6 +175,7 @@ export class DownloadManager {
|
|||||||
// Reset HF state
|
// Reset HF state
|
||||||
this.hfRepoId = null;
|
this.hfRepoId = null;
|
||||||
this.hfSelectedFiles = [];
|
this.hfSelectedFiles = [];
|
||||||
|
this.hfRepoCollapsed = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async retrieveVersionsForModel(modelId, source = null) {
|
async retrieveVersionsForModel(modelId, source = null) {
|
||||||
@@ -1078,7 +1080,7 @@ export class DownloadManager {
|
|||||||
|
|
||||||
showBatchPreviewStep() {
|
showBatchPreviewStep() {
|
||||||
document.querySelectorAll('.download-step').forEach(step => step.style.display = 'none');
|
document.querySelectorAll('.download-step').forEach(step => step.style.display = 'none');
|
||||||
document.getElementById('batchPreviewStep').style.display = 'block';
|
document.getElementById('batchPreviewStep').style.display = 'flex';
|
||||||
|
|
||||||
const validCount = this.batchModels.filter(m => {
|
const validCount = this.batchModels.filter(m => {
|
||||||
if (m.error) return false;
|
if (m.error) return false;
|
||||||
@@ -1092,56 +1094,36 @@ export class DownloadManager {
|
|||||||
const list = document.getElementById('batchPreviewList');
|
const list = document.getElementById('batchPreviewList');
|
||||||
const hasHfItems = this.batchModels.some(m => m.source === 'huggingface' && !m.error);
|
const hasHfItems = this.batchModels.some(m => m.source === 'huggingface' && !m.error);
|
||||||
|
|
||||||
let itemsHtml = this.batchModels.map((item, index) => {
|
// Error items render flat, outside any group
|
||||||
if (item.error) {
|
const errorItemsHtml = this.batchModels.map((item, index) => {
|
||||||
return `
|
if (!item.error) return null;
|
||||||
<div class="batch-preview-item batch-preview-error" data-index="${index}">
|
return `
|
||||||
<div class="batch-preview-icon">
|
<div class="batch-preview-item batch-preview-error" data-index="${index}">
|
||||||
<i class="fas fa-exclamation-triangle"></i>
|
<div class="batch-preview-icon">
|
||||||
</div>
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
<div class="batch-preview-info">
|
|
||||||
<div class="batch-preview-name">${item.url}</div>
|
|
||||||
<div class="batch-preview-meta batch-preview-error-text">${item.error}</div>
|
|
||||||
</div>
|
|
||||||
<button class="batch-preview-remove" data-index="${index}" title="${translate('common.actions.remove', {}, 'Remove')}">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
<div class="batch-preview-info">
|
||||||
}
|
<div class="batch-preview-name">${item.url}</div>
|
||||||
|
<div class="batch-preview-meta batch-preview-error-text">${item.error}</div>
|
||||||
|
</div>
|
||||||
|
<button class="batch-preview-remove" data-index="${index}" title="${translate('common.actions.remove', {}, 'Remove')}">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).filter(Boolean).join('');
|
||||||
|
|
||||||
|
// CivitAI items render flat, outside any group (unchanged)
|
||||||
|
const civitaiItemsHtml = this.batchModels.map((item, index) => {
|
||||||
|
if (item.error) return null;
|
||||||
|
if (item.source === 'huggingface') return null;
|
||||||
const ver = item.selectedVersion;
|
const ver = item.selectedVersion;
|
||||||
|
|
||||||
// HF batch item rendering with checkbox
|
|
||||||
if (item.source === 'huggingface') {
|
|
||||||
const hfSize = item.fileSizeBytes
|
|
||||||
? formatFileSize(item.fileSizeBytes)
|
|
||||||
: '?';
|
|
||||||
return `
|
|
||||||
<div class="batch-preview-item" data-index="${index}">
|
|
||||||
<input type="checkbox" class="batch-preview-checkbox"
|
|
||||||
data-index="${index}" ${item.checked !== false ? 'checked' : ''} />
|
|
||||||
<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-meta">
|
|
||||||
<span>${hfSize}</span>
|
|
||||||
<span>${item.repo || ''}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="batch-preview-remove" data-index="${index}" title="${translate('common.actions.remove', {}, 'Remove')}">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const firstImage = ver?.images?.find(img => !img.url.endsWith('.mp4'));
|
const firstImage = ver?.images?.find(img => !img.url.endsWith('.mp4'));
|
||||||
const thumbnailUrl = firstImage ? firstImage.url : '/loras_static/images/no-preview.png';
|
const thumbnailUrl = firstImage ? firstImage.url : '/loras_static/images/no-preview.png';
|
||||||
const fileSize = ver?.modelSizeKB
|
const fileSize = ver?.modelSizeKB
|
||||||
? (ver.modelSizeKB / 1024).toFixed(1)
|
? (ver.modelSizeKB / 1024).toFixed(1)
|
||||||
: (ver?.files?.[0]?.sizeKB ? (ver.files[0].sizeKB / 1024).toFixed(1) : '?');
|
: (ver?.files?.[0]?.sizeKB ? (ver.files[0].sizeKB / 1024).toFixed(1) : '?');
|
||||||
const existsLocally = ver?.existsLocally;
|
const existsLocally = ver?.existsLocally;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="batch-preview-item ${existsLocally ? 'batch-preview-local' : ''}" data-index="${index}">
|
<div class="batch-preview-item ${existsLocally ? 'batch-preview-local' : ''}" data-index="${index}">
|
||||||
<div class="batch-preview-thumbnail">
|
<div class="batch-preview-thumbnail">
|
||||||
@@ -1162,8 +1144,59 @@ export class DownloadManager {
|
|||||||
` : ''}
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
}).filter(Boolean).join('');
|
||||||
|
|
||||||
|
// Group HF items by repo (data model stays flat — only rendering groups)
|
||||||
|
const hfGroups = {};
|
||||||
|
this.batchModels.forEach((item, index) => {
|
||||||
|
if (item.error || item.source !== 'huggingface') return;
|
||||||
|
const repo = item.repo || 'unknown';
|
||||||
|
if (!hfGroups[repo]) hfGroups[repo] = [];
|
||||||
|
hfGroups[repo].push({ item, index });
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderHfItem = ({ item, index }) => {
|
||||||
|
const hfSize = item.fileSizeBytes ? formatFileSize(item.fileSizeBytes) : '?';
|
||||||
|
return `
|
||||||
|
<div class="batch-preview-item" data-index="${index}">
|
||||||
|
<input type="checkbox" class="batch-preview-checkbox"
|
||||||
|
data-index="${index}" ${item.checked !== false ? 'checked' : ''} />
|
||||||
|
<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-meta">
|
||||||
|
<span>${hfSize}</span>
|
||||||
|
<span>${item.repo || ''}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="batch-preview-remove" data-index="${index}" title="${translate('common.actions.remove', {}, 'Remove')}">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hfGroupsHtml = Object.keys(hfGroups).map(repo => {
|
||||||
|
const items = hfGroups[repo];
|
||||||
|
const isCollapsed = this.hfRepoCollapsed[repo] === true;
|
||||||
|
const allChecked = items.every(({ item }) => item.checked !== false);
|
||||||
|
const fileCount = items.length;
|
||||||
|
return `
|
||||||
|
<div class="batch-preview-group" data-repo="${repo}">
|
||||||
|
<div class="batch-preview-group-header">
|
||||||
|
<i class="fas fa-chevron-right batch-preview-group-toggle ${isCollapsed ? '' : 'expanded'}"></i>
|
||||||
|
<span class="batch-preview-group-name">${repo}</span>
|
||||||
|
<span class="batch-preview-group-count">${fileCount} ${translate('modals.download.fileSelection.files', {}, 'files')}</span>
|
||||||
|
<input type="checkbox" class="batch-preview-group-select-all" data-repo="${repo}" ${allChecked ? 'checked' : ''} />
|
||||||
|
</div>
|
||||||
|
<div class="batch-preview-group-body ${isCollapsed ? '' : 'expanded'}">
|
||||||
|
${items.map(renderHfItem).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
let itemsHtml = errorItemsHtml + civitaiItemsHtml + hfGroupsHtml;
|
||||||
|
|
||||||
// Prepend select-all toolbar if there are HF items with checkboxes
|
// Prepend select-all toolbar if there are HF items with checkboxes
|
||||||
if (hasHfItems) {
|
if (hasHfItems) {
|
||||||
const allChecked = this.batchModels
|
const allChecked = this.batchModels
|
||||||
@@ -1179,7 +1212,90 @@ export class DownloadManager {
|
|||||||
|
|
||||||
list.innerHTML = itemsHtml;
|
list.innerHTML = itemsHtml;
|
||||||
|
|
||||||
|
const updateCountAndSelectAll = () => {
|
||||||
|
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);
|
||||||
|
// Global select-all
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
// Per-group select-all
|
||||||
|
list.querySelectorAll('.batch-preview-group-select-all').forEach(gsa => {
|
||||||
|
const repo = gsa.dataset.repo;
|
||||||
|
const repoItems = this.batchModels.filter(m => m.source === 'huggingface' && !m.error && m.repo === repo);
|
||||||
|
gsa.checked = repoItems.length > 0 && repoItems.every(m => m.checked !== false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
list.onclick = (e) => {
|
list.onclick = (e) => {
|
||||||
|
// Per-group select-all checkbox
|
||||||
|
const groupSelectAll = e.target.closest('.batch-preview-group-select-all');
|
||||||
|
if (groupSelectAll) {
|
||||||
|
const repo = groupSelectAll.dataset.repo;
|
||||||
|
const checked = groupSelectAll.checked;
|
||||||
|
this.batchModels.forEach((m, idx) => {
|
||||||
|
if (m.source === 'huggingface' && !m.error && m.repo === repo) {
|
||||||
|
m.checked = checked;
|
||||||
|
const cb = list.querySelector(`.batch-preview-checkbox[data-index="${idx}"]`);
|
||||||
|
if (cb) cb.checked = checked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateCountAndSelectAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = e.target.closest('.batch-preview-group-header');
|
||||||
|
if (header) {
|
||||||
|
const group = header.closest('.batch-preview-group');
|
||||||
|
const repo = group.dataset.repo;
|
||||||
|
const body = group.querySelector('.batch-preview-group-body');
|
||||||
|
const toggle = group.querySelector('.batch-preview-group-toggle');
|
||||||
|
const isCollapsed = this.hfRepoCollapsed[repo];
|
||||||
|
if (isCollapsed) {
|
||||||
|
this.hfRepoCollapsed[repo] = false;
|
||||||
|
body.style.transition = ''; // restore in case collapse was interrupted
|
||||||
|
body.classList.add('expanded');
|
||||||
|
toggle.classList.add('expanded');
|
||||||
|
// force reflow so expanded class is registered before setting height
|
||||||
|
void body.offsetHeight;
|
||||||
|
body.style.maxHeight = body.scrollHeight + 'px';
|
||||||
|
const onEnd = (e) => {
|
||||||
|
if (e.propertyName !== 'max-height') return;
|
||||||
|
if (this.hfRepoCollapsed[repo] !== false) return;
|
||||||
|
body.style.maxHeight = ''; // fall back to .expanded's 9999px
|
||||||
|
body.removeEventListener('transitionend', onEnd);
|
||||||
|
};
|
||||||
|
body.addEventListener('transitionend', onEnd);
|
||||||
|
} else {
|
||||||
|
this.hfRepoCollapsed[repo] = true;
|
||||||
|
body.style.maxHeight = body.scrollHeight + 'px';
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
// animate only max-height; keep expanded so opacity stays 1
|
||||||
|
body.style.transition = 'max-height 0.35s ease';
|
||||||
|
body.style.maxHeight = '0';
|
||||||
|
toggle.classList.remove('expanded');
|
||||||
|
const onEnd = (e) => {
|
||||||
|
if (e.propertyName !== 'max-height') return;
|
||||||
|
if (this.hfRepoCollapsed[repo] !== true) return; // state changed since
|
||||||
|
body.classList.remove('expanded');
|
||||||
|
body.style.transition = '';
|
||||||
|
body.removeEventListener('transitionend', onEnd);
|
||||||
|
};
|
||||||
|
body.addEventListener('transitionend', onEnd);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const removeBtn = e.target.closest('.batch-preview-remove');
|
const removeBtn = e.target.closest('.batch-preview-remove');
|
||||||
if (removeBtn) {
|
if (removeBtn) {
|
||||||
const idx = parseInt(removeBtn.dataset.index);
|
const idx = parseInt(removeBtn.dataset.index);
|
||||||
@@ -1194,7 +1310,7 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Checkbox handler for HF batch items
|
// Individual HF checkbox handler
|
||||||
const checkboxes = list.querySelectorAll('.batch-preview-checkbox');
|
const checkboxes = list.querySelectorAll('.batch-preview-checkbox');
|
||||||
checkboxes.forEach(cb => {
|
checkboxes.forEach(cb => {
|
||||||
cb.addEventListener('change', (e) => {
|
cb.addEventListener('change', (e) => {
|
||||||
@@ -1202,26 +1318,11 @@ 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 and Next button
|
updateCountAndSelectAll();
|
||||||
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);
|
|
||||||
// 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
|
// Global select-all handler
|
||||||
const selectAll = document.getElementById('batchSelectAll');
|
const selectAll = document.getElementById('batchSelectAll');
|
||||||
if (selectAll) {
|
if (selectAll) {
|
||||||
selectAll.addEventListener('change', (e) => {
|
selectAll.addEventListener('change', (e) => {
|
||||||
@@ -1234,16 +1335,7 @@ export class DownloadManager {
|
|||||||
this.batchModels[idx].checked = checked;
|
this.batchModels[idx].checked = checked;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Update valid count in title and Next button
|
updateCountAndSelectAll();
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user