From 5b8b243aeee7a5b0be90120691dfc990af067698 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Mon, 3 Aug 2026 16:24:52 +0200 Subject: [PATCH] refactor: unify download queue rendering and add workflowBack icon --- web/css/resolver-shell.css | 5 + web/resolver/actions/queue_methods.js | 17 +++- .../actions/resolve_download_methods.js | 99 ++++++++++--------- web/resolver/resolver_dialog.js | 2 +- web/utils/icon_utils.js | 3 +- 5 files changed, 70 insertions(+), 56 deletions(-) diff --git a/web/css/resolver-shell.css b/web/css/resolver-shell.css index 7752412..8a68355 100644 --- a/web/css/resolver-shell.css +++ b/web/css/resolver-shell.css @@ -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); } diff --git a/web/resolver/actions/queue_methods.js b/web/resolver/actions/queue_methods.js index b3648cb..0012708 100644 --- a/web/resolver/actions/queue_methods.js +++ b/web/resolver/actions/queue_methods.js @@ -1173,12 +1173,15 @@ export const queueMethods = { }); }, - renderQueueDownloadsHtml(downloads) { + renderQueueDownloadsHtml( + downloads, + { wrap = true, showWorkflowAction = true, showWorkflowDetails = true } = {} + ) { if (!downloads.length) { return '
No active downloads.
'; } - let html = '
'; + let html = wrap ? '
' : ''; 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 += `
`; html += `
`; @@ -1275,10 +1280,12 @@ export const queueMethods = { html += ``; html += ``; html += ``; - html += `
Workflow${this.escapeHtml([workflowLabel, ...detailParts].filter(Boolean).join(' · '))}
`; + if (showWorkflowDetails) { + html += `
Workflow${this.escapeHtml([workflowLabel, ...detailParts].filter(Boolean).join(' · '))}
`; + } html += ``; } - html += ''; + if (wrap) html += ''; return html; }, diff --git a/web/resolver/actions/resolve_download_methods.js b/web/resolver/actions/resolve_download_methods.js index 026f309..20a1a54 100644 --- a/web/resolver/actions/resolve_download_methods.js +++ b/web/resolver/actions/resolve_download_methods.js @@ -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' : ''}` - : 'Connecting...'); - 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 - ? `` - : ``; - actionsHtml = `${toggleButton}`; - } - 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 }); } diff --git a/web/resolver/resolver_dialog.js b/web/resolver/resolver_dialog.js index 4a61876..c5e4871 100644 --- a/web/resolver/resolver_dialog.js +++ b/web/resolver/resolver_dialog.js @@ -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" }) ]), diff --git a/web/utils/icon_utils.js b/web/utils/icon_utils.js index 14de397..49ecc03 100644 --- a/web/utils/icon_utils.js +++ b/web/utils/icon_utils.js @@ -31,8 +31,9 @@ const SVG_ICONS = { shield: ``, copy: ``, externalLink: ``, - moreCircle: ``, + moreCircle: ``, internalLink: ``, + workflowBack: ``, database: ``, wrench: ``, comfyui: ``,