mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
refactor: update duplicate recipe management; simplify UI and remove deprecated functions
This commit is contained in:
@@ -291,7 +291,7 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: var(--space-1);
|
padding: var(--space-1);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var (--border-radius-sm);
|
||||||
background: var(--lora-surface);
|
background: var(--lora-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,14 +752,14 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
background: oklch(var(--lora-accent) / 0.1);
|
background: oklch(var(--lora-warning) / 0.1);
|
||||||
border: 1px solid var(--lora-accent);
|
border: 1px solid var(--lora-warning);
|
||||||
border-radius: var(--border-radius-sm) var(--border-radius-sm) 0 0;
|
border-radius: var(--border-radius-sm) var(--border-radius-sm) 0 0;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-warning .warning-icon {
|
.duplicate-warning .warning-icon {
|
||||||
color: var(--lora-accent);
|
color: var(--lora-warning);
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
@@ -776,59 +776,67 @@
|
|||||||
.duplicate-warning .warning-text {
|
.duplicate-warning .warning-text {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-duplicates-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--lora-warning);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-duplicates-btn:hover {
|
||||||
|
background: oklch(var(--lora-warning) / 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipes-list {
|
.duplicate-recipes-list {
|
||||||
max-height: 250px;
|
display: grid;
|
||||||
overflow-y: auto;
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-top: none;
|
border-top: none;
|
||||||
border-radius: 0 0 var(--border-radius-sm) var(--border-radius-sm);
|
border-radius: 0 0 var(--border-radius-sm) var(--border-radius-sm);
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: max-height 0.3s ease, padding 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-item {
|
.duplicate-recipes-list.collapsed {
|
||||||
display: flex;
|
max-height: 0;
|
||||||
gap: var(--space-2);
|
padding: 0 16px;
|
||||||
padding: var(--space-2);
|
overflow: hidden;
|
||||||
border-bottom: 1px solid var(--border-color);
|
}
|
||||||
|
|
||||||
|
.duplicate-recipe-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: background-color 0.2s;
|
border-radius: var(--border-radius-sm);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-item:last-child {
|
.duplicate-recipe-card:hover {
|
||||||
border-bottom: none;
|
transform: translateY(-2px);
|
||||||
}
|
|
||||||
|
|
||||||
.duplicate-recipe-item:hover {
|
|
||||||
background: var(--lora-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.duplicate-recipe-item.marked-for-deletion {
|
|
||||||
background: rgba(var(--lora-error-rgb), 0.1);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.duplicate-recipe-item.marked-for-deletion::before {
|
|
||||||
content: 'Will be deleted';
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 5px;
|
|
||||||
background: var(--lora-error);
|
|
||||||
color: white;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: var(--border-radius-xs);
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-preview {
|
.duplicate-recipe-preview {
|
||||||
width: 80px;
|
width: 100%;
|
||||||
height: 80px;
|
position: relative;
|
||||||
flex-shrink: 0;
|
aspect-ratio: 2/3;
|
||||||
border-radius: var(--border-radius-xs);
|
|
||||||
overflow: hidden;
|
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-preview img {
|
.duplicate-recipe-preview img {
|
||||||
@@ -837,18 +845,17 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.duplicate-recipe-title {
|
.duplicate-recipe-title {
|
||||||
font-weight: 500;
|
position: absolute;
|
||||||
font-size: 1em;
|
bottom: 0;
|
||||||
margin-bottom: 4px;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 8px;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1.3;
|
||||||
|
max-height: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
@@ -856,10 +863,13 @@
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-info {
|
.duplicate-recipe-details {
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
font-size: 0.75em;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
justify-content: space-between;
|
||||||
font-size: 0.85em;
|
align-items: center;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
@@ -871,52 +881,13 @@
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-recipe-actions {
|
/* Remove the old duplicate styles that are no longer needed */
|
||||||
display: flex;
|
.duplicate-recipe-item,
|
||||||
justify-content: center;
|
.duplicate-recipe-content,
|
||||||
align-items: center;
|
.duplicate-recipe-actions,
|
||||||
min-width: 80px;
|
.danger-btn,
|
||||||
}
|
|
||||||
|
|
||||||
.duplicate-recipe-actions button {
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 0.85em;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
white-space: nowrap;
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-btn {
|
|
||||||
background: var(--lora-error) !important;
|
|
||||||
color: white !important;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-btn:hover {
|
|
||||||
background: oklch(from var(--lora-error) l c h / 0.9) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-recipe-btn {
|
.view-recipe-btn {
|
||||||
background: var(--lora-surface) !important;
|
/* These styles are being replaced by the card layout */
|
||||||
color: var(--text-color) !important;
|
|
||||||
border: 1px solid var(--border-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-recipe-btn:hover {
|
|
||||||
background: var(--lora-accent) !important;
|
|
||||||
color: var(--lora-text) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-btn {
|
|
||||||
background: var(--lora-warning) !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-btn:hover {
|
|
||||||
background: oklch(from var(--lora-warning) l c h / 0.9) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modal buttons layout to accommodate multiple buttons */
|
/* Modal buttons layout to accommodate multiple buttons */
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ export class ImportManager {
|
|||||||
|
|
||||||
const earlyAccessWarning = document.getElementById('earlyAccessWarning');
|
const earlyAccessWarning = document.getElementById('earlyAccessWarning');
|
||||||
if (earlyAccessWarning) earlyAccessWarning.remove();
|
if (earlyAccessWarning) earlyAccessWarning.remove();
|
||||||
|
|
||||||
|
// Reset duplicate related properties
|
||||||
|
this.duplicateRecipes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleImportMode(mode) {
|
toggleImportMode(mode) {
|
||||||
@@ -246,44 +249,21 @@ export class ImportManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks or unmarks a duplicate recipe for deletion
|
* NOTE: This function is no longer needed with the simplified duplicates flow.
|
||||||
* @param {string} recipeId - The ID of the recipe to mark/unmark
|
* We're keeping it as a no-op stub to avoid breaking existing code that might call it.
|
||||||
* @param {HTMLElement} buttonElement - The button element that was clicked
|
|
||||||
*/
|
*/
|
||||||
markDuplicateForDeletion(recipeId, buttonElement) {
|
markDuplicateForDeletion(recipeId, buttonElement) {
|
||||||
// Initialize recipesToDelete array if it doesn't exist
|
// This functionality has been removed
|
||||||
if (!this.recipesToDelete) {
|
console.log('markDuplicateForDeletion is deprecated');
|
||||||
this.recipesToDelete = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the recipe item container
|
|
||||||
const recipeItem = buttonElement.closest('.duplicate-recipe-item');
|
|
||||||
if (!recipeItem) return;
|
|
||||||
|
|
||||||
// Check if this recipe is already marked for deletion
|
|
||||||
const isMarked = this.recipesToDelete.includes(recipeId);
|
|
||||||
|
|
||||||
if (isMarked) {
|
|
||||||
// Unmark the recipe
|
|
||||||
this.recipesToDelete = this.recipesToDelete.filter(id => id !== recipeId);
|
|
||||||
recipeItem.classList.remove('marked-for-deletion');
|
|
||||||
buttonElement.innerHTML = '<i class="fas fa-trash"></i> Delete';
|
|
||||||
} else {
|
|
||||||
// Mark the recipe for deletion
|
|
||||||
this.recipesToDelete.push(recipeId);
|
|
||||||
recipeItem.classList.add('marked-for-deletion');
|
|
||||||
buttonElement.innerHTML = '<i class="fas fa-undo"></i> Keep';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports the recipe as new, ignoring duplicates
|
* NOTE: This function is no longer needed with the simplified duplicates flow.
|
||||||
|
* We're keeping it as a no-op stub to avoid breaking existing code that might call it.
|
||||||
*/
|
*/
|
||||||
importRecipeAnyway() {
|
importRecipeAnyway() {
|
||||||
// Set flag to indicate we're importing as a new recipe
|
// This functionality has been simplified
|
||||||
this.importAsNew = true;
|
// Just proceed with normal flow
|
||||||
|
|
||||||
// Proceed with normal flow but skip duplicate replacement
|
|
||||||
this.proceedFromDetails();
|
this.proceedFromDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -226,28 +226,30 @@ export class RecipeDataManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate the HTML for duplicate recipes
|
// Generate the HTML for duplicate recipes warning
|
||||||
duplicateContainer.innerHTML = `
|
duplicateContainer.innerHTML = `
|
||||||
<div class="duplicate-warning">
|
<div class="duplicate-warning">
|
||||||
<div class="warning-icon"><i class="fas fa-clone"></i></div>
|
<div class="warning-icon"><i class="fas fa-clone"></i></div>
|
||||||
<div class="warning-content">
|
<div class="warning-content">
|
||||||
<div class="warning-title">
|
<div class="warning-title">
|
||||||
${this.importManager.duplicateRecipes.length} similar ${this.importManager.duplicateRecipes.length === 1 ? 'recipe' : 'recipes'} found in your library
|
${this.importManager.duplicateRecipes.length} identical ${this.importManager.duplicateRecipes.length === 1 ? 'recipe' : 'recipes'} found in your library
|
||||||
</div>
|
</div>
|
||||||
<div class="warning-text">
|
<div class="warning-text">
|
||||||
These recipes contain the same LoRAs with similar weights.
|
These recipes contain the same LoRAs with identical weights.
|
||||||
|
<button id="toggleDuplicatesList" class="toggle-duplicates-btn">
|
||||||
|
Show duplicates <i class="fas fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="duplicate-recipes-list">
|
<div class="duplicate-recipes-list collapsed">
|
||||||
${this.importManager.duplicateRecipes.map((recipe, index) => `
|
${this.importManager.duplicateRecipes.map((recipe) => `
|
||||||
<div class="duplicate-recipe-item" data-recipe-id="${recipe.id}">
|
<div class="duplicate-recipe-card">
|
||||||
<div class="duplicate-recipe-preview">
|
<div class="duplicate-recipe-preview">
|
||||||
<img src="${recipe.file_url}" alt="Recipe preview">
|
<img src="${recipe.file_url}" alt="Recipe preview">
|
||||||
</div>
|
|
||||||
<div class="duplicate-recipe-content">
|
|
||||||
<div class="duplicate-recipe-title">${recipe.title}</div>
|
<div class="duplicate-recipe-title">${recipe.title}</div>
|
||||||
<div class="duplicate-recipe-info">
|
</div>
|
||||||
|
<div class="duplicate-recipe-details">
|
||||||
<div class="duplicate-recipe-date">
|
<div class="duplicate-recipe-date">
|
||||||
<i class="fas fa-calendar-alt"></i> ${formatDate(recipe.modified)}
|
<i class="fas fa-calendar-alt"></i> ${formatDate(recipe.modified)}
|
||||||
</div>
|
</div>
|
||||||
@@ -256,12 +258,6 @@ export class RecipeDataManager {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="duplicate-recipe-actions">
|
|
||||||
<button class="danger-btn delete-recipe-btn" onclick="importManager.markDuplicateForDeletion('${recipe.id}', this)">
|
|
||||||
<i class="fas fa-trash"></i> Delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('')}
|
`).join('')}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -269,9 +265,23 @@ export class RecipeDataManager {
|
|||||||
// Show the duplicate container
|
// Show the duplicate container
|
||||||
duplicateContainer.style.display = 'block';
|
duplicateContainer.style.display = 'block';
|
||||||
|
|
||||||
// Initialize deletion tracking if not already done
|
// Add click event for the toggle button
|
||||||
if (!this.importManager.recipesToDelete) {
|
const toggleButton = document.getElementById('toggleDuplicatesList');
|
||||||
this.importManager.recipesToDelete = [];
|
if (toggleButton) {
|
||||||
|
toggleButton.addEventListener('click', () => {
|
||||||
|
const list = duplicateContainer.querySelector('.duplicate-recipes-list');
|
||||||
|
if (list) {
|
||||||
|
list.classList.toggle('collapsed');
|
||||||
|
const icon = toggleButton.querySelector('i');
|
||||||
|
if (icon) {
|
||||||
|
if (list.classList.contains('collapsed')) {
|
||||||
|
toggleButton.innerHTML = `Show duplicates <i class="fas fa-chevron-down"></i>`;
|
||||||
|
} else {
|
||||||
|
toggleButton.innerHTML = `Hide duplicates <i class="fas fa-chevron-up"></i>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No duplicates, hide the container if it exists
|
// No duplicates, hide the container if it exists
|
||||||
@@ -280,9 +290,8 @@ export class RecipeDataManager {
|
|||||||
duplicateContainer.style.display = 'none';
|
duplicateContainer.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset deletion tracking
|
// Reset duplicate tracking
|
||||||
this.importManager.duplicateRecipes = [];
|
this.importManager.duplicateRecipes = [];
|
||||||
this.importManager.recipesToDelete = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,52 +353,12 @@ export class RecipeDataManager {
|
|||||||
buttonsContainer.parentNode.insertBefore(warningContainer, buttonsContainer);
|
buttonsContainer.parentNode.insertBefore(warningContainer, buttonsContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicates to adjust button actions
|
// Check for duplicates but don't change button actions
|
||||||
const hasDuplicates = this.importManager.duplicateRecipes &&
|
|
||||||
this.importManager.duplicateRecipes.length > 0;
|
|
||||||
|
|
||||||
// If we have missing LoRAs (not deleted), show "Download Missing LoRAs"
|
|
||||||
// Otherwise show appropriate action based on duplicates
|
|
||||||
const missingNotDeleted = this.importManager.recipeData.loras.filter(
|
const missingNotDeleted = this.importManager.recipeData.loras.filter(
|
||||||
lora => !lora.existsLocally && !lora.isDeleted
|
lora => !lora.existsLocally && !lora.isDeleted
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
// All LoRAs exist locally
|
// Standard button behavior regardless of duplicates
|
||||||
const allLorasExist = missingNotDeleted === 0;
|
|
||||||
|
|
||||||
if (hasDuplicates) {
|
|
||||||
if (missingNotDeleted > 0) {
|
|
||||||
// We have both duplicates and missing LoRAs
|
|
||||||
nextButton.textContent = 'Download Missing LoRAs';
|
|
||||||
|
|
||||||
// Add "Import Anyway" as a secondary option
|
|
||||||
const importAnywayButton = document.createElement('button');
|
|
||||||
importAnywayButton.id = 'importAnywayBtn';
|
|
||||||
importAnywayButton.className = 'secondary-btn';
|
|
||||||
importAnywayButton.innerHTML = '<i class="fas fa-plus"></i> Import as New Recipe';
|
|
||||||
importAnywayButton.onclick = () => this.importManager.importRecipeAnyway();
|
|
||||||
|
|
||||||
// Insert after the back button
|
|
||||||
const backButton = document.querySelector('#detailsStep .secondary-btn');
|
|
||||||
actionsContainer.insertBefore(importAnywayButton, backButton.nextSibling);
|
|
||||||
} else {
|
|
||||||
// All LoRAs exist locally, but we have duplicates
|
|
||||||
nextButton.textContent = 'Replace Existing Recipe';
|
|
||||||
nextButton.classList.add('warning-btn');
|
|
||||||
|
|
||||||
// Add "Import as New" as a secondary option
|
|
||||||
const importAsNewButton = document.createElement('button');
|
|
||||||
importAsNewButton.id = 'importAnywayBtn';
|
|
||||||
importAsNewButton.className = 'secondary-btn';
|
|
||||||
importAsNewButton.innerHTML = '<i class="fas fa-plus"></i> Import as New Recipe';
|
|
||||||
importAsNewButton.onclick = () => this.importManager.importRecipeAnyway();
|
|
||||||
|
|
||||||
// Insert after the back button
|
|
||||||
const backButton = document.querySelector('#detailsStep .secondary-btn');
|
|
||||||
actionsContainer.insertBefore(importAsNewButton, backButton.nextSibling);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No duplicates, standard behavior
|
|
||||||
nextButton.classList.remove('warning-btn');
|
nextButton.classList.remove('warning-btn');
|
||||||
|
|
||||||
if (missingNotDeleted > 0) {
|
if (missingNotDeleted > 0) {
|
||||||
@@ -398,7 +367,6 @@ export class RecipeDataManager {
|
|||||||
nextButton.textContent = 'Save Recipe';
|
nextButton.textContent = 'Save Recipe';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
addTag() {
|
addTag() {
|
||||||
const tagInput = document.getElementById('tagInput');
|
const tagInput = document.getElementById('tagInput');
|
||||||
@@ -451,58 +419,10 @@ export class RecipeDataManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process any recipes marked for deletion
|
|
||||||
if (this.importManager.recipesToDelete && this.importManager.recipesToDelete.length > 0) {
|
|
||||||
this.deleteMarkedRecipes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update missing LoRAs list to exclude deleted LoRAs
|
// Update missing LoRAs list to exclude deleted LoRAs
|
||||||
this.importManager.missingLoras = this.importManager.recipeData.loras.filter(lora =>
|
this.importManager.missingLoras = this.importManager.recipeData.loras.filter(lora =>
|
||||||
!lora.existsLocally && !lora.isDeleted);
|
!lora.existsLocally && !lora.isDeleted);
|
||||||
|
|
||||||
// Check if we're replacing a duplicate or proceeding normally
|
|
||||||
const hasDuplicates = this.importManager.duplicateRecipes &&
|
|
||||||
this.importManager.duplicateRecipes.length > 0;
|
|
||||||
|
|
||||||
// Store replacement flag in importManager
|
|
||||||
this.importManager.isReplacingDuplicate = hasDuplicates &&
|
|
||||||
this.importManager.missingLoras.length === 0 &&
|
|
||||||
!this.importManager.importAsNew;
|
|
||||||
|
|
||||||
// Check for early access loras and show warning if any exist
|
|
||||||
const earlyAccessLoras = this.importManager.missingLoras.filter(lora => lora.isEarlyAccess);
|
|
||||||
if (earlyAccessLoras.length > 0) {
|
|
||||||
// Show a warning about early access loras
|
|
||||||
const warningMessage = `
|
|
||||||
<div class="early-access-warning">
|
|
||||||
<div class="warning-icon"><i class="fas fa-clock"></i></div>
|
|
||||||
<div class="warning-content">
|
|
||||||
<div class="warning-title">${earlyAccessLoras.length} LoRA(s) require Early Access</div>
|
|
||||||
<div class="warning-text">
|
|
||||||
These LoRAs require a payment to access. Download will fail if you haven't purchased access.
|
|
||||||
You may need to log in to your Civitai account in browser settings.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Show the warning message
|
|
||||||
const buttonsContainer = document.querySelector('#detailsStep .modal-actions');
|
|
||||||
if (buttonsContainer) {
|
|
||||||
// Remove existing warning if any
|
|
||||||
const existingWarning = document.getElementById('earlyAccessWarning');
|
|
||||||
if (existingWarning) {
|
|
||||||
existingWarning.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new warning
|
|
||||||
const warningContainer = document.createElement('div');
|
|
||||||
warningContainer.id = 'earlyAccessWarning';
|
|
||||||
warningContainer.innerHTML = warningMessage;
|
|
||||||
buttonsContainer.parentNode.insertBefore(warningContainer, buttonsContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have downloadable missing LoRAs, go to location step
|
// If we have downloadable missing LoRAs, go to location step
|
||||||
if (this.importManager.missingLoras.length > 0) {
|
if (this.importManager.missingLoras.length > 0) {
|
||||||
// Store only downloadable LoRAs for the download step
|
// Store only downloadable LoRAs for the download step
|
||||||
@@ -513,46 +433,4 @@ export class RecipeDataManager {
|
|||||||
this.importManager.saveRecipe();
|
this.importManager.saveRecipe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteMarkedRecipes() {
|
|
||||||
if (!this.importManager.recipesToDelete || this.importManager.recipesToDelete.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Show loading indicator
|
|
||||||
this.importManager.loadingManager.showSimpleLoading('Deleting marked recipes...');
|
|
||||||
|
|
||||||
// Call API to delete recipes
|
|
||||||
const response = await fetch('/api/recipes/bulk-delete', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
recipe_ids: this.importManager.recipesToDelete
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const error = await response.json();
|
|
||||||
throw new Error(error.error || 'Failed to delete recipes');
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
const deletedCount = this.importManager.recipesToDelete.length;
|
|
||||||
showToast(`Successfully deleted ${deletedCount} ${deletedCount === 1 ? 'recipe' : 'recipes'}`, 'success');
|
|
||||||
|
|
||||||
// Reset the delete list
|
|
||||||
this.importManager.recipesToDelete = [];
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error deleting recipes:', error);
|
|
||||||
showToast('Failed to delete recipes: ' + error.message, 'error');
|
|
||||||
} finally {
|
|
||||||
this.importManager.loadingManager.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,11 +78,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Container for duplicate recipes warning -->
|
|
||||||
<div id="duplicateRecipesContainer" class="duplicate-recipes-container" style="display: none;">
|
|
||||||
<!-- Duplicate recipes will be populated here -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>LoRAs in this Recipe <span id="loraCountInfo" class="lora-count-info">(0/0 in library)</span></label>
|
<label>LoRAs in this Recipe <span id="loraCountInfo" class="lora-count-info">(0/0 in library)</span></label>
|
||||||
<div id="lorasList" class="loras-list">
|
<div id="lorasList" class="loras-list">
|
||||||
@@ -90,6 +85,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Container for duplicate recipes warning -->
|
||||||
|
<div id="duplicateRecipesContainer" class="duplicate-recipes-container" style="display: none;">
|
||||||
|
<!-- Duplicate recipes will be populated here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button class="secondary-btn" onclick="importManager.backToUpload()">Back</button>
|
<button class="secondary-btn" onclick="importManager.backToUpload()">Back</button>
|
||||||
<button class="primary-btn" onclick="importManager.proceedFromDetails()">Next</button>
|
<button class="primary-btn" onclick="importManager.proceedFromDetails()">Next</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user