mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-26 15:38:52 -03:00
feat(import): add import-only option for recipes without downloading missing LoRAs
Add dual-button design in recipe import flow: - Details step: [Import Recipe Only] [Import & Download] - Location step: [Back] [Import & Download] (removed redundant Import Only) Changes: - templates/components/import_modal.html: Add secondary button for import-only - static/js/managers/ImportManager.js: Add saveRecipeOnlyFromDetails() method - static/js/managers/import/RecipeDataManager.js: Update button state management - static/js/managers/import/DownloadManager.js: Support skipDownload flag - locales/*.json: Complete all translation TODOs Closes #868
This commit is contained in:
@@ -142,6 +142,28 @@ export class ImportManager {
|
||||
|
||||
// Reset duplicate related properties
|
||||
this.duplicateRecipes = [];
|
||||
|
||||
// Reset button visibility in location step
|
||||
this.resetLocationStepButtons();
|
||||
}
|
||||
|
||||
resetLocationStepButtons() {
|
||||
// Reset buttons to default state
|
||||
const locationStep = document.getElementById('locationStep');
|
||||
if (!locationStep) return;
|
||||
|
||||
const backBtn = locationStep.querySelector('.secondary-btn');
|
||||
const primaryBtn = locationStep.querySelector('.primary-btn');
|
||||
|
||||
// Back button - show
|
||||
if (backBtn) {
|
||||
backBtn.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
// Primary button - reset text
|
||||
if (primaryBtn) {
|
||||
primaryBtn.textContent = translate('recipes.controls.import.downloadAndSaveRecipe', {}, 'Download & Save Recipe');
|
||||
}
|
||||
}
|
||||
|
||||
toggleImportMode(mode) {
|
||||
@@ -261,11 +283,57 @@ export class ImportManager {
|
||||
this.loadDefaultPathSetting();
|
||||
|
||||
this.updateTargetPath();
|
||||
|
||||
// Update download button with missing LoRA count (if any)
|
||||
if (this.missingLoras && this.missingLoras.length > 0) {
|
||||
this.updateDownloadButtonCount();
|
||||
this.updateImportButtonsVisibility(true);
|
||||
} else {
|
||||
this.updateImportButtonsVisibility(false);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('toast.recipes.importFailed', { message: error.message }, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
updateImportButtonsVisibility(hasMissingLoras) {
|
||||
// Update primary button text based on whether there are missing LoRAs
|
||||
const locationStep = document.getElementById('locationStep');
|
||||
if (!locationStep) return;
|
||||
|
||||
const backBtn = locationStep.querySelector('.secondary-btn');
|
||||
const primaryBtn = locationStep.querySelector('.primary-btn');
|
||||
|
||||
// Back button - always show
|
||||
if (backBtn) {
|
||||
backBtn.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
// Update primary button text
|
||||
if (primaryBtn) {
|
||||
const downloadCountSpan = locationStep.querySelector('#downloadLoraCount');
|
||||
if (hasMissingLoras) {
|
||||
// Rebuild button content to ensure proper structure
|
||||
const buttonText = translate('recipes.controls.import.importAndDownload', {}, 'Import & Download');
|
||||
primaryBtn.innerHTML = `${buttonText} <span id="downloadLoraCount"></span>`;
|
||||
} else {
|
||||
primaryBtn.textContent = translate('recipes.controls.import.downloadAndSaveRecipe', {}, 'Download & Save Recipe');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateDownloadButtonCount() {
|
||||
// Update the download count badge on the primary button
|
||||
const locationStep = document.getElementById('locationStep');
|
||||
if (!locationStep) return;
|
||||
|
||||
const downloadCountSpan = locationStep.querySelector('#downloadLoraCount');
|
||||
if (downloadCountSpan) {
|
||||
const missingCount = this.missingLoras?.length || 0;
|
||||
downloadCountSpan.textContent = missingCount > 0 ? `(${missingCount})` : '';
|
||||
}
|
||||
}
|
||||
|
||||
backToUpload() {
|
||||
this.stepManager.showStep('uploadStep');
|
||||
|
||||
@@ -426,12 +494,54 @@ export class ImportManager {
|
||||
const modalTitle = document.querySelector('#importModal h2');
|
||||
if (modalTitle) modalTitle.textContent = translate('recipes.controls.import.downloadMissingLoras', {}, 'Download Missing LoRAs');
|
||||
|
||||
// Update the save button text
|
||||
const saveButton = document.querySelector('#locationStep .primary-btn');
|
||||
if (saveButton) saveButton.textContent = translate('recipes.controls.import.downloadMissingLoras', {}, 'Download Missing LoRAs');
|
||||
// Update button texts and show download count
|
||||
const locationStep = document.getElementById('locationStep');
|
||||
if (!locationStep) return;
|
||||
|
||||
const primaryBtn = locationStep.querySelector('.primary-btn');
|
||||
const backBtn = locationStep.querySelector('.secondary-btn');
|
||||
|
||||
// primaryBtn should be the "Import & Download" button
|
||||
if (primaryBtn) {
|
||||
const buttonText = translate('recipes.controls.import.importAndDownload', {}, 'Import & Download');
|
||||
primaryBtn.innerHTML = `${buttonText} <span id="downloadLoraCount">(${recipeData.loras?.length || 0})</span>`;
|
||||
}
|
||||
|
||||
// Hide the "Back" button in download-only mode
|
||||
if (backBtn) {
|
||||
backBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the back button
|
||||
const backButton = document.querySelector('#locationStep .secondary-btn');
|
||||
if (backButton) backButton.style.display = 'none';
|
||||
saveRecipeWithoutDownload() {
|
||||
// Call save recipe with skip download flag
|
||||
return this.downloadManager.saveRecipe(true);
|
||||
}
|
||||
|
||||
async saveRecipeOnlyFromDetails() {
|
||||
// Validate recipe name first
|
||||
if (!this.recipeName) {
|
||||
showToast('toast.recipes.enterRecipeName', {}, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark deleted LoRAs as excluded
|
||||
if (this.recipeData && this.recipeData.loras) {
|
||||
this.recipeData.loras.forEach(lora => {
|
||||
if (lora.isDeleted) {
|
||||
lora.exclude = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update missing LoRAs list
|
||||
this.missingLoras = this.recipeData.loras.filter(lora =>
|
||||
!lora.existsLocally && !lora.isDeleted);
|
||||
|
||||
// For import only, we don't need downloadableLoRAs
|
||||
this.downloadableLoRAs = [];
|
||||
|
||||
// Save recipe without downloading
|
||||
await this.downloadManager.saveRecipe(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export class DownloadManager {
|
||||
this.importManager = importManager;
|
||||
}
|
||||
|
||||
async saveRecipe() {
|
||||
async saveRecipe(skipDownload = false) {
|
||||
// Check if we're in download-only mode (for existing recipe)
|
||||
const isDownloadOnly = !!this.importManager.recipeId;
|
||||
|
||||
@@ -20,7 +20,10 @@ export class DownloadManager {
|
||||
|
||||
try {
|
||||
// Show progress indicator
|
||||
this.importManager.loadingManager.showSimpleLoading(isDownloadOnly ? translate('recipes.controls.import.downloadingLoras', {}, 'Downloading LoRAs...') : translate('recipes.controls.import.savingRecipe', {}, 'Saving recipe...'));
|
||||
const loadingMessage = skipDownload
|
||||
? translate('recipes.controls.import.savingRecipe', {}, 'Saving recipe...')
|
||||
: (isDownloadOnly ? translate('recipes.controls.import.downloadingLoras', {}, 'Downloading LoRAs...') : translate('recipes.controls.import.savingRecipe', {}, 'Saving recipe...'));
|
||||
this.importManager.loadingManager.showSimpleLoading(loadingMessage);
|
||||
|
||||
// Only send the complete recipe to save if not in download-only mode
|
||||
if (!isDownloadOnly) {
|
||||
@@ -98,15 +101,17 @@ export class DownloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we need to download LoRAs
|
||||
// Check if we need to download LoRAs (skip if skipDownload is true)
|
||||
let failedDownloads = 0;
|
||||
if (this.importManager.downloadableLoRAs && this.importManager.downloadableLoRAs.length > 0) {
|
||||
if (!skipDownload && this.importManager.downloadableLoRAs && this.importManager.downloadableLoRAs.length > 0) {
|
||||
await this.downloadMissingLoras();
|
||||
}
|
||||
|
||||
// Show success message
|
||||
if (isDownloadOnly) {
|
||||
if (failedDownloads === 0) {
|
||||
if (skipDownload) {
|
||||
showToast('toast.recipes.recipeSaved', {}, 'success');
|
||||
} else if (failedDownloads === 0) {
|
||||
showToast('toast.loras.downloadSuccessful', {}, 'success');
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -325,7 +325,8 @@ export class RecipeDataManager {
|
||||
}
|
||||
|
||||
updateNextButtonState() {
|
||||
const nextButton = document.querySelector('#detailsStep .primary-btn');
|
||||
const nextButton = document.getElementById('nextBtn');
|
||||
const importOnlyBtn = document.getElementById('importOnlyBtn');
|
||||
const actionsContainer = document.querySelector('#detailsStep .modal-actions');
|
||||
if (!nextButton || !actionsContainer) return;
|
||||
|
||||
@@ -365,7 +366,7 @@ export class RecipeDataManager {
|
||||
buttonsContainer.parentNode.insertBefore(warningContainer, buttonsContainer);
|
||||
}
|
||||
|
||||
// Check for duplicates but don't change button actions
|
||||
// Check for downloadable missing LoRAs
|
||||
const missingNotDeleted = this.importManager.recipeData.loras.filter(
|
||||
lora => !lora.existsLocally && !lora.isDeleted
|
||||
).length;
|
||||
@@ -374,8 +375,16 @@ export class RecipeDataManager {
|
||||
nextButton.classList.remove('warning-btn');
|
||||
|
||||
if (missingNotDeleted > 0) {
|
||||
nextButton.textContent = translate('recipes.controls.import.downloadMissingLoras', {}, 'Download Missing LoRAs');
|
||||
// Show import only button and update primary button
|
||||
if (importOnlyBtn) {
|
||||
importOnlyBtn.style.display = 'inline-block';
|
||||
}
|
||||
nextButton.textContent = translate('recipes.controls.import.importAndDownload', {}, 'Import & Download') + ` (${missingNotDeleted})`;
|
||||
} else {
|
||||
// Hide import only button and show save recipe
|
||||
if (importOnlyBtn) {
|
||||
importOnlyBtn.style.display = 'none';
|
||||
}
|
||||
nextButton.textContent = translate('recipes.controls.import.saveRecipe', {}, 'Save Recipe');
|
||||
}
|
||||
}
|
||||
@@ -440,8 +449,11 @@ export class RecipeDataManager {
|
||||
// Store only downloadable LoRAs for the download step
|
||||
this.importManager.downloadableLoRAs = this.importManager.missingLoras;
|
||||
this.importManager.proceedToLocation();
|
||||
} else if (this.importManager.missingLoras.length === 0 && this.importManager.recipeData.loras.some(l => !l.existsLocally)) {
|
||||
// All missing LoRAs are deleted, save recipe without download
|
||||
this.importManager.saveRecipe();
|
||||
} else {
|
||||
// Otherwise, save the recipe directly
|
||||
// No missing LoRAs at all, save the recipe directly
|
||||
this.importManager.saveRecipe();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user