mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
Add reconnect functionality for deleted LoRAs in recipe modal
- Introduced a new API endpoint to reconnect deleted LoRAs to local files. - Updated RecipeModal to include UI elements for reconnecting LoRAs, including input fields and buttons. - Enhanced CSS styles for deleted badges and reconnect containers to improve user experience. - Implemented event handling for reconnect actions, including input validation and API calls. - Updated recipe data handling to reflect changes after reconnecting LoRAs.
This commit is contained in:
@@ -89,7 +89,7 @@ class LoraRoutes:
|
|||||||
settings=settings, # Pass settings to template
|
settings=settings, # Pass settings to template
|
||||||
request=request # Pass the request object to the template
|
request=request # Pass the request object to the template
|
||||||
)
|
)
|
||||||
logger.info(f"Loras page loaded successfully with {len(cache.raw_data)} items")
|
logger.debug(f"Loras page loaded successfully with {len(cache.raw_data)} items")
|
||||||
except Exception as cache_error:
|
except Exception as cache_error:
|
||||||
logger.error(f"Error loading cache data: {cache_error}")
|
logger.error(f"Error loading cache data: {cache_error}")
|
||||||
# 如果获取缓存失败,也显示初始化页面
|
# 如果获取缓存失败,也显示初始化页面
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ class RecipeRoutes:
|
|||||||
# Add new endpoint for updating recipe metadata (name and tags)
|
# Add new endpoint for updating recipe metadata (name and tags)
|
||||||
app.router.add_put('/api/recipe/{recipe_id}/update', routes.update_recipe)
|
app.router.add_put('/api/recipe/{recipe_id}/update', routes.update_recipe)
|
||||||
|
|
||||||
|
# Add new endpoint for reconnecting deleted LoRAs
|
||||||
|
app.router.add_post('/api/recipe/lora/reconnect', routes.reconnect_lora)
|
||||||
|
|
||||||
# Start cache initialization
|
# Start cache initialization
|
||||||
app.on_startup.append(routes._init_cache)
|
app.on_startup.append(routes._init_cache)
|
||||||
|
|
||||||
@@ -762,7 +765,7 @@ class RecipeRoutes:
|
|||||||
return web.json_response({"error": "Invalid workflow JSON"}, status=400)
|
return web.json_response({"error": "Invalid workflow JSON"}, status=400)
|
||||||
|
|
||||||
if not workflow_json:
|
if not workflow_json:
|
||||||
return web.json_response({"error": "Missing required workflow_json field"}, status=400)
|
return web.json_response({"error": "Missing workflow JSON"}, status=400)
|
||||||
|
|
||||||
# Find the latest image in the temp directory
|
# Find the latest image in the temp directory
|
||||||
temp_dir = config.temp_directory
|
temp_dir = config.temp_directory
|
||||||
@@ -1021,3 +1024,113 @@ class RecipeRoutes:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error updating recipe: {e}", exc_info=True)
|
logger.error(f"Error updating recipe: {e}", exc_info=True)
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
async def reconnect_lora(self, request: web.Request) -> web.Response:
|
||||||
|
"""Reconnect a deleted LoRA in a recipe to a local LoRA file"""
|
||||||
|
try:
|
||||||
|
# Parse request data
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
# Validate required fields
|
||||||
|
required_fields = ['recipe_id', 'lora_data', 'target_name']
|
||||||
|
for field in required_fields:
|
||||||
|
if field not in data:
|
||||||
|
return web.json_response({
|
||||||
|
"error": f"Missing required field: {field}"
|
||||||
|
}, status=400)
|
||||||
|
|
||||||
|
recipe_id = data['recipe_id']
|
||||||
|
lora_data = data['lora_data']
|
||||||
|
target_name = data['target_name']
|
||||||
|
|
||||||
|
# Get recipe scanner
|
||||||
|
scanner = self.recipe_scanner
|
||||||
|
lora_scanner = scanner._lora_scanner
|
||||||
|
|
||||||
|
# Check if recipe exists
|
||||||
|
recipe_path = os.path.join(scanner.recipes_dir, f"{recipe_id}.recipe.json")
|
||||||
|
if not os.path.exists(recipe_path):
|
||||||
|
return web.json_response({"error": "Recipe not found"}, status=404)
|
||||||
|
|
||||||
|
# Find target LoRA by name
|
||||||
|
target_lora = await lora_scanner.get_lora_info_by_name(target_name)
|
||||||
|
if not target_lora:
|
||||||
|
return web.json_response({"error": f"Local LoRA not found with name: {target_name}"}, status=404)
|
||||||
|
|
||||||
|
# Load recipe data
|
||||||
|
with open(recipe_path, 'r', encoding='utf-8') as f:
|
||||||
|
recipe_data = json.load(f)
|
||||||
|
|
||||||
|
# Find the deleted LoRA in the recipe
|
||||||
|
found = False
|
||||||
|
updated_lora = None
|
||||||
|
|
||||||
|
# Identification can be by hash, modelVersionId, or modelName
|
||||||
|
for i, lora in enumerate(recipe_data.get('loras', [])):
|
||||||
|
match_found = False
|
||||||
|
|
||||||
|
# Try to match by available identifiers
|
||||||
|
if 'hash' in lora and 'hash' in lora_data and lora['hash'] == lora_data['hash']:
|
||||||
|
match_found = True
|
||||||
|
elif 'modelVersionId' in lora and 'modelVersionId' in lora_data and lora['modelVersionId'] == lora_data['modelVersionId']:
|
||||||
|
match_found = True
|
||||||
|
elif 'modelName' in lora and 'modelName' in lora_data and lora['modelName'] == lora_data['modelName']:
|
||||||
|
match_found = True
|
||||||
|
|
||||||
|
if match_found:
|
||||||
|
# Update LoRA data
|
||||||
|
lora['isDeleted'] = False
|
||||||
|
lora['file_name'] = target_name
|
||||||
|
|
||||||
|
# Update with information from the target LoRA
|
||||||
|
if 'sha256' in target_lora:
|
||||||
|
lora['hash'] = target_lora['sha256'].lower()
|
||||||
|
if target_lora.get("civitai"):
|
||||||
|
lora['modelName'] = target_lora['civitai']['model']['name']
|
||||||
|
lora['modelVersionName'] = target_lora['civitai']['name']
|
||||||
|
lora['modelVersionId'] = target_lora['civitai']['id']
|
||||||
|
|
||||||
|
# Keep original fields for identification
|
||||||
|
|
||||||
|
# Mark as found and store updated lora
|
||||||
|
found = True
|
||||||
|
updated_lora = dict(lora) # Make a copy for response
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
return web.json_response({"error": "Could not find matching deleted LoRA in recipe"}, status=404)
|
||||||
|
|
||||||
|
# Save updated recipe
|
||||||
|
with open(recipe_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(recipe_data, f, indent=4, ensure_ascii=False)
|
||||||
|
|
||||||
|
updated_lora['inLibrary'] = True
|
||||||
|
updated_lora['preview_url'] = target_lora['preview_url']
|
||||||
|
updated_lora['localPath'] = target_lora['file_path']
|
||||||
|
|
||||||
|
# Update in cache if it exists
|
||||||
|
if scanner._cache is not None:
|
||||||
|
for cache_item in scanner._cache.raw_data:
|
||||||
|
if cache_item.get('id') == recipe_id:
|
||||||
|
# Replace loras array with updated version
|
||||||
|
cache_item['loras'] = recipe_data['loras']
|
||||||
|
|
||||||
|
# Resort the cache
|
||||||
|
asyncio.create_task(scanner._cache.resort())
|
||||||
|
break
|
||||||
|
|
||||||
|
# Update EXIF metadata if image exists
|
||||||
|
image_path = recipe_data.get('file_path')
|
||||||
|
if image_path and os.path.exists(image_path):
|
||||||
|
from ..utils.exif_utils import ExifUtils
|
||||||
|
ExifUtils.append_recipe_metadata(image_path, recipe_data)
|
||||||
|
|
||||||
|
return web.json_response({
|
||||||
|
"success": True,
|
||||||
|
"recipe_id": recipe_id,
|
||||||
|
"updated_lora": updated_lora
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error reconnecting LoRA: {e}", exc_info=True)
|
||||||
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|||||||
@@ -584,7 +584,7 @@
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deleted badge */
|
/* Deleted badge with reconnect functionality */
|
||||||
.deleted-badge {
|
.deleted-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -603,6 +603,138 @@
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add reconnect functionality styles */
|
||||||
|
.deleted-badge.reconnectable {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleted-badge.reconnectable:hover {
|
||||||
|
background-color: var(--lora-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleted-badge .reconnect-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: var(--z-overlay);
|
||||||
|
width: max-content;
|
||||||
|
max-width: 200px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: normal;
|
||||||
|
top: calc(100% + 5px);
|
||||||
|
left: 0;
|
||||||
|
margin-left: -100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleted-badge.reconnectable:hover .reconnect-tooltip {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LoRA reconnect container */
|
||||||
|
.lora-reconnect-container {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--lora-surface);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
padding: 12px;
|
||||||
|
margin-top: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lora-reconnect-container.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-instructions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-instructions p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.95em;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-instructions small {
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.7;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-instructions code {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .reconnect-instructions code {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-input {
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
background: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-cancel-btn,
|
||||||
|
.reconnect-confirm-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
font-size: 0.85em;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-cancel-btn {
|
||||||
|
background: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-confirm-btn {
|
||||||
|
background: var(--lora-accent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-cancel-btn:hover {
|
||||||
|
background: var(--lora-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reconnect-confirm-btn:hover {
|
||||||
|
background: color-mix(in oklch, var(--lora-accent), black 10%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Recipe status partial state */
|
/* Recipe status partial state */
|
||||||
.recipe-status.partial {
|
.recipe-status.partial {
|
||||||
background: rgba(127, 127, 127, 0.1);
|
background: rgba(127, 127, 127, 0.1);
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ class RecipeModal {
|
|||||||
!event.target.closest('.edit-icon')) {
|
!event.target.closest('.edit-icon')) {
|
||||||
this.saveTagsEdit();
|
this.saveTagsEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle reconnect input
|
||||||
|
const reconnectContainers = document.querySelectorAll('.lora-reconnect-container');
|
||||||
|
reconnectContainers.forEach(container => {
|
||||||
|
if (container.classList.contains('active') &&
|
||||||
|
!container.contains(event.target) &&
|
||||||
|
!event.target.closest('.deleted-badge.reconnectable')) {
|
||||||
|
this.hideReconnectInput(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,8 +368,9 @@ class RecipeModal {
|
|||||||
</div>`;
|
</div>`;
|
||||||
} else if (isDeleted) {
|
} else if (isDeleted) {
|
||||||
localStatus = `
|
localStatus = `
|
||||||
<div class="deleted-badge">
|
<div class="deleted-badge reconnectable" data-lora-index="${recipe.loras.indexOf(lora)}">
|
||||||
<i class="fas fa-trash-alt"></i> Deleted
|
<span class="badge-text"><i class="fas fa-trash-alt"></i> Deleted</span>
|
||||||
|
<div class="reconnect-tooltip">Click to reconnect with a local LoRA</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
} else {
|
} else {
|
||||||
localStatus = `
|
localStatus = `
|
||||||
@@ -387,7 +398,7 @@ class RecipeModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="${loraItemClass}">
|
<div class="${loraItemClass}" data-lora-index="${recipe.loras.indexOf(lora)}">
|
||||||
<div class="recipe-lora-thumbnail">
|
<div class="recipe-lora-thumbnail">
|
||||||
${previewMedia}
|
${previewMedia}
|
||||||
</div>
|
</div>
|
||||||
@@ -401,11 +412,29 @@ class RecipeModal {
|
|||||||
<div class="recipe-lora-weight">Weight: ${lora.strength || 1.0}</div>
|
<div class="recipe-lora-weight">Weight: ${lora.strength || 1.0}</div>
|
||||||
${lora.baseModel ? `<div class="base-model">${lora.baseModel}</div>` : ''}
|
${lora.baseModel ? `<div class="base-model">${lora.baseModel}</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="lora-reconnect-container" data-lora-index="${recipe.loras.indexOf(lora)}">
|
||||||
|
<div class="reconnect-instructions">
|
||||||
|
<p>Enter LoRA Syntax or Name to Reconnect:</p>
|
||||||
|
<small>Example: <code><lora:Boris_Vallejo_BV_flux_D:1></code> or just <code>Boris_Vallejo_BV_flux_D</code></small>
|
||||||
|
</div>
|
||||||
|
<div class="reconnect-form">
|
||||||
|
<input type="text" class="reconnect-input" placeholder="Enter LoRA name or syntax">
|
||||||
|
<div class="reconnect-actions">
|
||||||
|
<button class="reconnect-cancel-btn">Cancel</button>
|
||||||
|
<button class="reconnect-confirm-btn">Reconnect</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
// Add event listeners for reconnect functionality
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setupReconnectButtons();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
// Generate recipe syntax for copy button (this is now a placeholder, actual syntax will be fetched from the API)
|
// Generate recipe syntax for copy button (this is now a placeholder, actual syntax will be fetched from the API)
|
||||||
this.recipeLorasSyntax = '';
|
this.recipeLorasSyntax = '';
|
||||||
|
|
||||||
@@ -829,6 +858,155 @@ class RecipeModal {
|
|||||||
state.loadingManager.hide();
|
state.loadingManager.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New methods for reconnecting LoRAs
|
||||||
|
setupReconnectButtons() {
|
||||||
|
// Add event listeners to all deleted badges
|
||||||
|
const deletedBadges = document.querySelectorAll('.deleted-badge.reconnectable');
|
||||||
|
deletedBadges.forEach(badge => {
|
||||||
|
badge.addEventListener('mouseenter', () => {
|
||||||
|
badge.querySelector('.badge-text').innerHTML = 'Reconnect';
|
||||||
|
});
|
||||||
|
|
||||||
|
badge.addEventListener('mouseleave', () => {
|
||||||
|
badge.querySelector('.badge-text').innerHTML = '<i class="fas fa-trash-alt"></i> Deleted';
|
||||||
|
});
|
||||||
|
|
||||||
|
badge.addEventListener('click', (e) => {
|
||||||
|
const loraIndex = badge.getAttribute('data-lora-index');
|
||||||
|
this.showReconnectInput(loraIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add event listeners to reconnect cancel buttons
|
||||||
|
const cancelButtons = document.querySelectorAll('.reconnect-cancel-btn');
|
||||||
|
cancelButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', (e) => {
|
||||||
|
const container = button.closest('.lora-reconnect-container');
|
||||||
|
this.hideReconnectInput(container);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add event listeners to reconnect confirm buttons
|
||||||
|
const confirmButtons = document.querySelectorAll('.reconnect-confirm-btn');
|
||||||
|
confirmButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', (e) => {
|
||||||
|
const container = button.closest('.lora-reconnect-container');
|
||||||
|
const input = container.querySelector('.reconnect-input');
|
||||||
|
const loraIndex = container.getAttribute('data-lora-index');
|
||||||
|
this.reconnectLora(loraIndex, input.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add keydown handlers to reconnect inputs
|
||||||
|
const reconnectInputs = document.querySelectorAll('.reconnect-input');
|
||||||
|
reconnectInputs.forEach(input => {
|
||||||
|
input.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const container = input.closest('.lora-reconnect-container');
|
||||||
|
const loraIndex = container.getAttribute('data-lora-index');
|
||||||
|
this.reconnectLora(loraIndex, input.value);
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
const container = input.closest('.lora-reconnect-container');
|
||||||
|
this.hideReconnectInput(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showReconnectInput(loraIndex) {
|
||||||
|
// Hide any currently active reconnect containers
|
||||||
|
document.querySelectorAll('.lora-reconnect-container.active').forEach(active => {
|
||||||
|
active.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show the reconnect container for this lora
|
||||||
|
const container = document.querySelector(`.lora-reconnect-container[data-lora-index="${loraIndex}"]`);
|
||||||
|
if (container) {
|
||||||
|
container.classList.add('active');
|
||||||
|
const input = container.querySelector('.reconnect-input');
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hideReconnectInput(container) {
|
||||||
|
if (container && container.classList.contains('active')) {
|
||||||
|
container.classList.remove('active');
|
||||||
|
const input = container.querySelector('.reconnect-input');
|
||||||
|
if (input) input.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async reconnectLora(loraIndex, inputValue) {
|
||||||
|
if (!inputValue || !inputValue.trim()) {
|
||||||
|
showToast('Please enter a LoRA name or syntax', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse input value to extract file_name
|
||||||
|
let loraSyntaxMatch = inputValue.match(/<lora:([^:>]+)(?::[^>]+)?>/);
|
||||||
|
let fileName = loraSyntaxMatch ? loraSyntaxMatch[1] : inputValue.trim();
|
||||||
|
|
||||||
|
// Remove any file extension if present
|
||||||
|
fileName = fileName.replace(/\.\w+$/, '');
|
||||||
|
|
||||||
|
// Get the deleted lora data
|
||||||
|
const deletedLora = this.currentRecipe.loras[loraIndex];
|
||||||
|
if (!deletedLora) {
|
||||||
|
showToast('Error: Could not find the LoRA in the recipe', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.loadingManager.showSimpleLoading('Reconnecting LoRA...');
|
||||||
|
|
||||||
|
// Call API to reconnect the LoRA
|
||||||
|
const response = await fetch('/api/recipe/lora/reconnect', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
recipe_id: this.recipeId,
|
||||||
|
lora_data: deletedLora,
|
||||||
|
target_name: fileName
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Hide the reconnect input
|
||||||
|
const container = document.querySelector(`.lora-reconnect-container[data-lora-index="${loraIndex}"]`);
|
||||||
|
this.hideReconnectInput(container);
|
||||||
|
|
||||||
|
// Update the current recipe with the updated lora data
|
||||||
|
this.currentRecipe.loras[loraIndex] = result.updated_lora;
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
showToast('LoRA reconnected successfully', 'success');
|
||||||
|
|
||||||
|
// Refresh modal to show updated content
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showRecipeDetails(this.currentRecipe);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Refresh recipes list
|
||||||
|
if (window.recipeManager && typeof window.recipeManager.loadRecipes === 'function') {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.recipeManager.loadRecipes(true);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast(`Error: ${result.error}`, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reconnecting LoRA:', error);
|
||||||
|
showToast(`Error reconnecting LoRA: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
state.loadingManager.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { RecipeModal };
|
export { RecipeModal };
|
||||||
Reference in New Issue
Block a user