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:
Will Miao
2026-03-25 11:56:34 +08:00
parent 8b85e083e2
commit e97648c70b
14 changed files with 700 additions and 542 deletions

View File

@@ -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 {

View File

@@ -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();
}
}