refactor: unify download queue rendering and add workflowBack icon

This commit is contained in:
Dariusz L
2026-08-03 16:24:52 +02:00
parent 415bcb93f4
commit 5b8b243aee
5 changed files with 70 additions and 56 deletions
+5
View File
@@ -682,6 +682,11 @@
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
}
/* The Missing Models preview already provides the panel's horizontal padding. */
#model-resolver-modal .mr-missing-detail-pane .mr-download-progress-slot .mr-download-queue-item {
margin: 0;
}
.mr-queue-item.mr-download-queue-item:last-child {
border-bottom: 1px solid var(--mr-border-strong);
}
+12 -5
View File
@@ -1173,12 +1173,15 @@ export const queueMethods = {
});
},
renderQueueDownloadsHtml(downloads) {
renderQueueDownloadsHtml(
downloads,
{ wrap = true, showWorkflowAction = true, showWorkflowDetails = true } = {}
) {
if (!downloads.length) {
return '<div class="mr-queue-empty">No active downloads.</div>';
}
let html = '<div class="mr-queue-items mr-download-queue-items">';
let html = wrap ? '<div class="mr-queue-items mr-download-queue-items">' : '';
for (const { downloadId, info } of downloads) {
const progress = typeof this.applyPendingDownloadStatus === 'function'
? this.applyPendingDownloadStatus(info, info.lastProgress || {})
@@ -1251,7 +1254,9 @@ export const queueMethods = {
);
html += actionButton('mr-download-queue-cancel', 'Cancel download', 'x');
html += actionButton('mr-download-queue-open-folder', 'Open download folder', 'folderOpen', !hasFolderAction);
html += actionButton('mr-download-queue-switch-workflow', 'Switch to workflow', 'internalLink', !hasWorkflowAction);
if (showWorkflowAction) {
html += actionButton('mr-download-queue-switch-workflow', 'Switch to workflow', 'workflowBack', !hasWorkflowAction);
}
html += actionButton('mr-download-queue-more', 'More download actions', 'info', !contextModel);
html += `</div>`;
html += `</div>`;
@@ -1275,10 +1280,12 @@ export const queueMethods = {
html += `</span>`;
html += `</div>`;
html += `</div>`;
html += `<div class="mr-download-queue-details" data-tooltip="${this.escapeHtml([targetPath, backend].filter(Boolean).join(' · '))}"><span>Workflow</span><span>${this.escapeHtml([workflowLabel, ...detailParts].filter(Boolean).join(' · '))}</span></div>`;
if (showWorkflowDetails) {
html += `<div class="mr-download-queue-details" data-tooltip="${this.escapeHtml([targetPath, backend].filter(Boolean).join(' · '))}"><span>Workflow</span><span>${this.escapeHtml([workflowLabel, ...detailParts].filter(Boolean).join(' · '))}</span></div>`;
}
html += `</div>`;
}
html += '</div>';
if (wrap) html += '</div>';
return html;
},
@@ -1091,29 +1091,59 @@ export const resolveDownloadMethods = {
if (event?.type === 'pointerdown') {
button._handledPointerAction = true;
}
handler();
handler(event);
};
button.addEventListener('pointerdown', run);
button.addEventListener('click', run);
};
progressDiv.querySelectorAll('.cancel-download-btn, .cancel-download-btn-pending').forEach(cancelBtn => {
progressDiv.querySelectorAll('.cancel-download-btn, .cancel-download-btn-pending, .mr-download-queue-cancel').forEach(cancelBtn => {
bindInstantAction(cancelBtn, () => {
const targetDownloadId = cancelBtn.dataset.downloadId || downloadId;
if (targetDownloadId) this.cancelDownload(targetDownloadId);
});
});
progressDiv.querySelectorAll('.pause-download-btn').forEach(pauseBtn => {
progressDiv.querySelectorAll('.pause-download-btn, .mr-download-queue-pause').forEach(pauseBtn => {
bindInstantAction(pauseBtn, () => {
const targetDownloadId = pauseBtn.dataset.downloadId || downloadId;
if (targetDownloadId) this.pauseDownload(targetDownloadId);
});
});
progressDiv.querySelectorAll('.resume-download-btn').forEach(resumeBtn => {
progressDiv.querySelectorAll('.resume-download-btn, .mr-download-queue-resume').forEach(resumeBtn => {
bindInstantAction(resumeBtn, () => {
const targetDownloadId = resumeBtn.dataset.downloadId || downloadId;
if (targetDownloadId) this.resumeDownload(targetDownloadId);
});
});
const getDownloadContext = (button) => {
const targetDownloadId = button?.dataset?.downloadId || downloadId;
const info = this.activeDownloads?.[targetDownloadId];
if (!targetDownloadId || !info) return null;
const progress = typeof this.applyPendingDownloadStatus === 'function'
? this.applyPendingDownloadStatus(info, info.lastProgress || {})
: (info.lastProgress || {});
const workflowLabel = this.getDownloadWorkflowLabel?.(info) || 'Current workflow';
return this.getDownloadQueueContext?.(progress, info, workflowLabel, targetDownloadId)
|| this.getDownloadFolderContext?.(progress, info)
|| null;
};
progressDiv.querySelectorAll('.mr-download-queue-open-folder').forEach(folderBtn => {
bindInstantAction(folderBtn, () => {
const context = getDownloadContext(folderBtn);
if (context) this.openContainingFolder?.(context);
});
});
progressDiv.querySelectorAll('.mr-download-queue-switch-workflow').forEach(workflowBtn => {
bindInstantAction(workflowBtn, () => {
const context = getDownloadContext(workflowBtn);
if (context) this.switchToDownloadWorkflow?.(context);
});
});
progressDiv.querySelectorAll('.mr-download-queue-more').forEach(moreBtn => {
bindInstantAction(moreBtn, (event) => {
const item = moreBtn.closest('.mr-download-queue-item');
if (item) window.MLOpenContextMenu?.(event, item);
});
});
},
renderDownloadSnapshotMarkup(downloadId, snapshot) {
@@ -1123,51 +1153,22 @@ export const resolveDownloadMethods = {
const shouldRenderProgress = this.isDownloadProgressStatus(status, isActive);
if (shouldRenderProgress) {
const canCancel = Boolean(downloadId);
const backend = String(progress.download_backend || snapshot.downloadBackend || snapshot.download_backend || '').toLowerCase();
const isAria2 = backend === 'aria2';
const isPaused = status === 'paused';
const presentation = this.getDownloadProgressPresentation(progress, { unknownTotal: 'Unknown' });
const {
percent,
downloadedText: downloaded,
totalText: total,
progressMeta,
percentLabel,
logicalDownloadedText: logicalDownloaded,
logicalTotalText: logicalTotal,
} = presentation;
const leftText = presentation.isFinalizing
? `Finalizing file: ${logicalDownloaded} / ${logicalTotal}`
: (presentation.totalSize
? `${downloaded} / ${total} (${percentLabel}%)${isPaused ? ' - Paused' : ''}`
: '<span class="mr-info-accent-text">Connecting...</span>');
let actionClass = canCancel ? 'cancel-download-btn mr-btn mr-btn-danger mr-btn-sm' : '';
let actionText = canCancel ? 'Cancel' : '';
let actionsHtml = '';
if (canCancel && isAria2 && isPaused) {
actionClass = 'resume-download-btn mr-btn mr-btn-primary mr-btn-sm';
actionText = 'Resume';
} else if (canCancel && isAria2 && status === 'downloading') {
actionClass = 'pause-download-btn mr-btn mr-btn-secondary mr-btn-sm';
actionText = 'Pause';
}
if (canCancel && isAria2 && (isPaused || status === 'downloading')) {
const safeDownloadId = this.escapeHtml(String(downloadId));
const toggleButton = isPaused
? `<button class="resume-download-btn mr-btn mr-btn-primary mr-btn-sm" data-download-id="${safeDownloadId}">Resume</button>`
: `<button class="pause-download-btn mr-btn mr-btn-secondary mr-btn-sm" data-download-id="${safeDownloadId}">Pause</button>`;
actionsHtml = `${toggleButton}<button class="cancel-download-btn mr-btn mr-btn-danger mr-btn-sm" data-download-id="${safeDownloadId}">Cancel</button>`;
}
return this.renderProgressWithAction({
percent,
leftText,
rightText: progressMeta,
actionClass,
actionText,
actionDataAttr: canCancel ? `data-download-id="${downloadId}"` : '',
actionsHtml,
contextMenuModel: this.getDownloadFolderContext(progress, snapshot)
const info = {
...snapshot,
missing: snapshot.missing || {},
lastProgress: progress,
lastStatus: status,
filename: progress.filename || snapshot.filename || 'Download',
category: snapshot.category || progress.category || '',
downloadBackend: progress.download_backend || snapshot.downloadBackend || snapshot.download_backend || '',
downloadDirectory: progress.directory || snapshot.downloadDirectory || '',
downloadPath: progress.path || snapshot.downloadPath || '',
workflowLabel: snapshot.workflowLabel || snapshot.workflow_label || 'Current workflow'
};
return this.renderQueueDownloadsHtml([{ downloadId, info }], {
wrap: false,
showWorkflowAction: false,
showWorkflowDetails: false
});
}
+1 -1
View File
@@ -222,7 +222,7 @@ export class ResolverManagerDialog extends ComfyDialog {
onclick: () => this.handleContextMenuAction('switchWorkflow')
}, [
$el("span.mr-context-menu-item-icon", {
innerHTML: getSvgIcon('internalLink', 'currentColor', 'mr-context-menu-item-svg')
innerHTML: getSvgIcon('workflowBack', 'currentColor', 'mr-context-menu-item-svg')
}),
$el("span", { textContent: "Switch to Workflow" })
]),
+2 -1
View File
@@ -31,8 +31,9 @@ const SVG_ICONS = {
shield: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"></path></svg>`,
copy: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`,
externalLink: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8.5 12h10.5"></path><path d="m15.25 15.75 3.75-3.75-3.75-3.75"></path><path d="M19 7.75V7a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-.75"></path></svg>`,
moreCircle: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="{color}" stroke-width="1.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></path></svg>`,
moreCircle: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="{color}" stroke-width="2" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></path></svg>`,
internalLink: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8.5 12h10.5"></path><path d="M12.25 15.75 8.5 12l3.75-3.75"></path><path d="M19 7.75V7a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-.75"></path></svg>`,
workflowBack: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="14" y="3" width="7.2" height="5.5" rx="1.6"></rect><rect x="14" y="15.5" width="7.2" height="5.5" rx="1.6"></rect><path d="M10.5 5.75H14"></path><path d="M10.5 18.25H14"></path><path d="M10.5 5.75v12.5"></path><path d="M10.5 12H3"></path><path d="m6 9-3 3 3 3"></path></svg>`,
database: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="12" cy="5" rx="8" ry="3"></ellipse><path d="M4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5"></path><path d="M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"></path></svg>`,
wrench: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z"></path></svg>`,
comfyui: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#f0ff41" aria-hidden="true"><path d="M5.485 23.76c-.568 0-1.026-.207-1.325-.598-.307-.402-.387-.964-.22-1.54l.672-2.315a.605.605 0 00-.1-.536.622.622 0 00-.494-.243H2.085c-.568 0-1.026-.207-1.325-.598-.307-.403-.387-.964-.22-1.54l2.31-7.917.255-.87c.343-1.18 1.592-2.14 2.786-2.14h2.313c.276 0 .519-.18.595-.442l.764-2.633C9.906 1.208 11.155.249 12.35.249l4.945-.008h3.62c.568 0 1.027.206 1.325.597.307.402.387.964.22 1.54l-1.035 3.566c-.343 1.178-1.593 2.137-2.787 2.137l-4.956.01H11.37a.618.618 0 00-.594.441l-1.928 6.604a.605.605 0 00.1.537c.118.153.3.243.495.243l3.275-.006h3.61c.568 0 1.026.206 1.325.598.307.402.387.964.22 1.54l-1.036 3.565c-.342 1.179-1.592 2.138-2.786 2.138l-4.957.01h-3.61z"></path></svg>`,