diff --git a/README.md b/README.md index e0be8e6..f53a6f5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Python 3.10+ JavaScript - SponsorGitHub + SponsorGitHub

🔹 Quick Start diff --git a/web/css/resolver-main.css b/web/css/resolver-main.css index 4a6d5fe..5314118 100644 --- a/web/css/resolver-main.css +++ b/web/css/resolver-main.css @@ -5390,11 +5390,15 @@ font-size: 11px; white-space: nowrap; } +#model-resolver-modal .mr-missing-detail-pane .mr-selected-actions { + gap: 8px; + margin-left: auto; +} #model-resolver-modal .mr-missing-detail-pane .model-resolver-selected .mr-btn { min-height: var(--mr-missing-search-button-height, 34px); height: var(--mr-missing-search-button-height, 34px); padding: 0 12px; - margin-left: auto; + margin-left: 0; background: var(--mr-missing-control-bg, rgba(255,255,255,0.035)); border-color: var(--mr-missing-control-border, rgba(255,255,255,0.105)); border-radius: var(--mr-missing-control-radius, 7px); @@ -5408,6 +5412,16 @@ border-color: var(--mr-missing-control-border-hover, rgba(255,255,255,0.17)); color: var(--mr-text); } +#model-resolver-modal .mr-missing-detail-pane .model-resolver-selected .mr-btn-primary { + background: rgba(47, 128, 237, 0.18); + border-color: rgba(47, 128, 237, 0.34); + color: #dbeafe; +} +#model-resolver-modal .mr-missing-detail-pane .model-resolver-selected .mr-btn-primary:hover:not(:disabled) { + background: rgba(47, 128, 237, 0.26); + border-color: rgba(47, 128, 237, 0.48); + color: #ffffff; +} #model-resolver-modal .mr-missing-detail-pane .mr-search-progress-list-compact { gap: 7px; margin: 10px 0 8px; diff --git a/web/css/resolver-shell.css b/web/css/resolver-shell.css index 98c8ae8..79c9a62 100644 --- a/web/css/resolver-shell.css +++ b/web/css/resolver-shell.css @@ -523,6 +523,14 @@ text-overflow: ellipsis; } +.mr-selected-actions { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + gap: var(--mr-spacing-8); + margin-left: auto; +} + .mr-inline-note-link { cursor: pointer; color: var(--mr-accent-blue); diff --git a/web/resolver/actions/queue_methods.js b/web/resolver/actions/queue_methods.js index 60794fe..875b679 100644 --- a/web/resolver/actions/queue_methods.js +++ b/web/resolver/actions/queue_methods.js @@ -343,17 +343,28 @@ export const queueMethods = { // Build selected bar content const label = selection.resolved_model?.relative_path || selection.resolved_model?.filename || selection.resolved_path || ''; - const resolveBtnId = `selected-remove-${nodeId}-${widgetIndex}`; + const applyBtnId = `selected-apply-${nodeId}-${widgetIndex}`; + const removeBtnId = `selected-remove-${nodeId}-${widgetIndex}`; selectedBar.innerHTML = `

`; selectedBar.innerHTML += `✓ Selected:`; selectedBar.innerHTML += `${label}`; - selectedBar.innerHTML += ``; + selectedBar.innerHTML += ``; + selectedBar.innerHTML += ``; + selectedBar.innerHTML += ``; + selectedBar.innerHTML += ``; selectedBar.innerHTML += `
`; selectedBar.style.display = 'block'; + const applyBtn = selectedBar.querySelector(`#${applyBtnId}`); + if (applyBtn) { + applyBtn.addEventListener('click', () => { + this.applyQueuedByKey(key, applyBtn); + }); + } + // Wire remove button - use key-based removal - const removeBtn = selectedBar.querySelector(`#${resolveBtnId}`); + const removeBtn = selectedBar.querySelector(`#${removeBtnId}`); if (removeBtn) { removeBtn.addEventListener('click', () => { this.removeQueuedByKey(key); @@ -725,10 +736,39 @@ export const queueMethods = { }, /** - * Apply all pending resolutions in batch + * Apply one queued resolution from the selected bar. */ - async applyPendingResolutions() { - const list = this.pendingResolutions || []; + async applyQueuedByKey(key, button = null) { + if (!key || !this.pendingIndex.has(key)) { + this.showNotification('Selected match is no longer queued.', 'error'); + return; + } + + const idx = this.pendingIndex.get(key); + const selection = this.pendingResolutions?.[idx]; + if (!selection) { + this.showNotification('Selected match is no longer queued.', 'error'); + return; + } + + if (button) { + button.disabled = true; + button.classList.add('mr-btn-is-disabled'); + button.textContent = 'Applying...'; + } + + try { + await this.applyPendingResolutionList([selection], { clearAll: false }); + } finally { + if (button?.isConnected) { + button.disabled = false; + button.classList.remove('mr-btn-is-disabled'); + button.textContent = 'Apply'; + } + } + }, + + async applyPendingResolutionList(list, { clearAll = false } = {}) { if (!list.length) { this.showNotification('No selections queued', 'error'); return; @@ -760,11 +800,19 @@ export const queueMethods = { const optimisticData = this.getOptimisticAnalysisDataAfterApply(appliedSelections); await this.updateWorkflowInComfyUI(data.workflow); - this.pendingResolutions = []; - this.pendingIndex = new Map(); + const appliedKeys = new Set(appliedSelections.map(selection => this.getResolutionQueueKey(selection))); + const remainingSelections = clearAll + ? [] + : (this.pendingResolutions || []).filter(selection => !appliedKeys.has(this.getResolutionQueueKey(selection))); + + this.pendingResolutions = remainingSelections; + this.rebuildPendingIndex(); this.savePendingQueueForActiveWorkflow(); this.syncWorkflowScopedQueue?.(data.workflow); + this.pendingResolutions = remainingSelections; + this.rebuildPendingIndex(); + this.savePendingQueueForActiveWorkflow(); this.updateApplyPendingButton(); this.updateQueuePanel(); @@ -785,6 +833,14 @@ export const queueMethods = { } }, + /** + * Apply all pending resolutions in batch + */ + async applyPendingResolutions() { + const list = this.pendingResolutions || []; + await this.applyPendingResolutionList(list, { clearAll: true }); + }, + updateApplyPendingButton() { if (!this.applyPendingBtn) return; const count = this.pendingResolutions?.length || 0;