mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
feat(recipes): add reconnect remediation paths for missing recipe LoRAs
- Snapshot pre-rematch entry state (reconnectSnapshot) so rematched entries can be undone via the existing restore flow - Bulk missing-LoRA downloads mark unresolvable failures hash-invalid, flipping those entries from download to reconnect candidacy - Recipe modal always offers a reconnect action next to download for missing LoRA entries - Rematch runs collect an opt-in relaxed-matching choice (also reconnect missing models by file name) via a pre-run options dialog on the global, bulk and single-recipe entries - L4 (filename-level) matches are listed in a results dialog with per-entry undo
This commit is contained in:
@@ -4,6 +4,7 @@ import { translate } from '../../utils/i18nHelpers.js';
|
||||
import { state } from '../../state/index.js';
|
||||
import { getCompleteApiConfig, getCurrentModelType } from '../../api/apiConfig.js';
|
||||
import { performModelUpdateCheck } from '../../utils/updateCheckHelpers.js';
|
||||
import { rematchModalManager } from '../../managers/RematchModalManager.js';
|
||||
|
||||
export class GlobalContextMenu extends BaseContextMenu {
|
||||
constructor() {
|
||||
@@ -368,6 +369,18 @@ export class GlobalContextMenu extends BaseContextMenu {
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect options (relaxed matching) before starting anything; the
|
||||
// run only begins when the user confirms the dialog.
|
||||
rematchModalManager.showOptionsModal({
|
||||
onConfirm: ({ relaxed }) => this._startRematch(menuItem, relaxed),
|
||||
});
|
||||
}
|
||||
|
||||
async _startRematch(menuItem, relaxed = false) {
|
||||
if (this._rematchInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._rematchInProgress = true;
|
||||
menuItem?.classList.add('disabled');
|
||||
|
||||
@@ -384,6 +397,7 @@ export class GlobalContextMenu extends BaseContextMenu {
|
||||
const response = await fetch('/api/lm/recipes/rematch', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ relaxed: !!relaxed }),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
@@ -458,6 +472,11 @@ export class GlobalContextMenu extends BaseContextMenu {
|
||||
if (window.recipesPage) {
|
||||
window.recipesPage.refresh();
|
||||
}
|
||||
// Filename-level (L4) matches are imprecise —
|
||||
// always surface them for review/undo.
|
||||
if (Array.isArray(p.l4_matches) && p.l4_matches.length > 0) {
|
||||
rematchModalManager.showResultsModal(p.l4_matches);
|
||||
}
|
||||
} else if (p.status === 'error') {
|
||||
throw new Error(p.error || 'Rematch failed');
|
||||
} else if (p.status === 'cancelled') {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { setSessionItem, removeSessionItem } from '../../utils/storageHelpers.js
|
||||
import { updateRecipeMetadata } from '../../api/recipeApi.js';
|
||||
import { state } from '../../state/index.js';
|
||||
import { moveManager } from '../../managers/MoveManager.js';
|
||||
import { rematchModalManager } from '../../managers/RematchModalManager.js';
|
||||
import { probeExtension, delegateReimport, getCivitaiImageInfo } from '../../utils/extensionReimportBridge.js';
|
||||
|
||||
export class RecipeContextMenu extends BaseContextMenu {
|
||||
@@ -303,11 +304,22 @@ export class RecipeContextMenu extends BaseContextMenu {
|
||||
// Capture before any await: the menu's click handler nulls currentCard
|
||||
const filePath = this.currentCard?.dataset?.filepath;
|
||||
|
||||
// Collect options (relaxed matching) before starting anything; the
|
||||
// run only begins when the user confirms the dialog.
|
||||
rematchModalManager.showOptionsModal({
|
||||
scope: 'single',
|
||||
onConfirm: ({ relaxed }) => this._startRematchRecipe(recipeId, filePath, relaxed),
|
||||
});
|
||||
}
|
||||
|
||||
async _startRematchRecipe(recipeId, filePath, relaxed = false) {
|
||||
try {
|
||||
showToast('Rematching recipe to local models...', {}, 'info');
|
||||
|
||||
const response = await fetch(`/api/lm/recipe/${recipeId}/rematch`, {
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ relaxed: !!relaxed }),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
@@ -330,6 +342,11 @@ export class RecipeContextMenu extends BaseContextMenu {
|
||||
state.virtualScroller.updateSingleItem(filePath, updatedRecipe);
|
||||
}
|
||||
}
|
||||
// Filename-level (L4) matches are imprecise — always
|
||||
// surface them for review/undo.
|
||||
if (Array.isArray(result.l4_matches) && result.l4_matches.length > 0) {
|
||||
rematchModalManager.showResultsModal(result.l4_matches);
|
||||
}
|
||||
} else if (result.unresolved_entries > 0) {
|
||||
// Entries existed but have no local model — expected for
|
||||
// models deleted from Civitai; informational, not an error.
|
||||
|
||||
Reference in New Issue
Block a user