mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Refactor card update functions to unify model and Lora card handling; remove unused metadata path update logic. See #228
This commit is contained in:
@@ -374,32 +374,6 @@ class LoraScanner(ModelScanner):
|
||||
|
||||
return letters
|
||||
|
||||
async def _update_metadata_paths(self, metadata_path: str, lora_path: str) -> Dict:
|
||||
"""Update file paths in metadata file"""
|
||||
try:
|
||||
with open(metadata_path, 'r', encoding='utf-8') as f:
|
||||
metadata = json.load(f)
|
||||
|
||||
# Update file_path
|
||||
metadata['file_path'] = lora_path.replace(os.sep, '/')
|
||||
|
||||
# Update preview_url if exists
|
||||
if 'preview_url' in metadata:
|
||||
preview_dir = os.path.dirname(lora_path)
|
||||
preview_name = os.path.splitext(os.path.basename(metadata['preview_url']))[0]
|
||||
preview_ext = os.path.splitext(metadata['preview_url'])[1]
|
||||
new_preview_path = os.path.join(preview_dir, f"{preview_name}{preview_ext}")
|
||||
metadata['preview_url'] = new_preview_path.replace(os.sep, '/')
|
||||
|
||||
# Save updated metadata
|
||||
with open(metadata_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(metadata, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return metadata
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating metadata paths: {e}", exc_info=True)
|
||||
|
||||
# Lora-specific hash index functionality
|
||||
def has_lora_hash(self, sha256: str) -> bool:
|
||||
"""Check if a LoRA with given hash exists"""
|
||||
|
||||
@@ -1042,7 +1042,7 @@ class ModelScanner:
|
||||
|
||||
metadata['file_path'] = model_path.replace(os.sep, '/')
|
||||
|
||||
if 'preview_url' in metadata:
|
||||
if 'preview_url' in metadata and metadata['preview_url']:
|
||||
preview_dir = os.path.dirname(model_path)
|
||||
preview_name = os.path.splitext(os.path.basename(metadata['preview_url']))[0]
|
||||
preview_ext = os.path.splitext(metadata['preview_url'])[1]
|
||||
|
||||
@@ -4,7 +4,6 @@ import { showLoraModal } from './loraModal/index.js';
|
||||
import { bulkManager } from '../managers/BulkManager.js';
|
||||
import { NSFW_LEVELS } from '../utils/constants.js';
|
||||
import { replacePreview, saveModelMetadata } from '../api/loraApi.js'
|
||||
import { showDeleteModal } from '../utils/modalUtils.js';
|
||||
|
||||
// Add a global event delegation handler
|
||||
export function setupLoraCardEventDelegation() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { BASE_MODELS } from '../../utils/constants.js';
|
||||
import { updateCheckpointCard } from '../../utils/cardUpdater.js';
|
||||
import { updateModelCard } from '../../utils/cardUpdater.js';
|
||||
import { saveModelMetadata, renameCheckpointFile } from '../../api/checkpointApi.js';
|
||||
|
||||
/**
|
||||
@@ -115,7 +115,7 @@ export function setupModelNameEditing(filePath) {
|
||||
await saveModelMetadata(filePath, { model_name: newModelName });
|
||||
|
||||
// Update the corresponding checkpoint card's dataset and display
|
||||
updateCheckpointCard(filePath, { model_name: newModelName });
|
||||
updateModelCard(filePath, { model_name: newModelName });
|
||||
|
||||
// BUGFIX: Directly update the card's dataset.name attribute to ensure
|
||||
// it's correctly read when reopening the modal
|
||||
@@ -301,7 +301,7 @@ async function saveBaseModel(filePath, originalValue) {
|
||||
await saveModelMetadata(filePath, { base_model: newBaseModel });
|
||||
|
||||
// Update the card with the new base model
|
||||
updateCheckpointCard(filePath, { base_model: newBaseModel });
|
||||
updateModelCard(filePath, { base_model: newBaseModel });
|
||||
|
||||
showToast('Base model updated successfully', 'success');
|
||||
} catch (error) {
|
||||
@@ -431,7 +431,7 @@ export function setupFileNameEditing(filePath) {
|
||||
const newFilePath = [...pathParts, newFileName].join('/');
|
||||
|
||||
// Update the checkpoint card with new file path
|
||||
updateCheckpointCard(filePath, {
|
||||
updateModelCard(filePath, {
|
||||
filepath: newFilePath,
|
||||
file_name: newFileName
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { saveModelMetadata } from '../../api/checkpointApi.js';
|
||||
import { updateCheckpointCard } from '../../utils/cardUpdater.js';
|
||||
|
||||
// Preset tag suggestions
|
||||
const PRESET_TAGS = [
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { setupTagEditMode } from './ModelTags.js'; // Add import for tag editing
|
||||
import { saveModelMetadata } from '../../api/checkpointApi.js';
|
||||
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
|
||||
import { updateCheckpointCard } from '../../utils/cardUpdater.js';
|
||||
import { updateModelCard } from '../../utils/cardUpdater.js';
|
||||
import { state } from '../../state/index.js';
|
||||
|
||||
/**
|
||||
@@ -264,7 +264,7 @@ async function saveNotes(filePath) {
|
||||
await saveModelMetadata(filePath, { notes: content });
|
||||
|
||||
// Update the corresponding checkpoint card's dataset
|
||||
updateCheckpointCard(filePath, { notes: content });
|
||||
updateModelCard(filePath, { notes: content });
|
||||
|
||||
showToast('Notes saved successfully', 'success');
|
||||
} catch (error) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { BASE_MODELS } from '../../utils/constants.js';
|
||||
import { updateLoraCard } from '../../utils/cardUpdater.js';
|
||||
import { updateModelCard } from '../../utils/cardUpdater.js';
|
||||
import { saveModelMetadata, renameLoraFile } from '../../api/loraApi.js';
|
||||
|
||||
/**
|
||||
@@ -116,7 +116,7 @@ export function setupModelNameEditing(filePath) {
|
||||
await saveModelMetadata(filePath, { model_name: newModelName });
|
||||
|
||||
// Update the corresponding lora card's dataset and display
|
||||
updateLoraCard(filePath, { model_name: newModelName });
|
||||
updateModelCard(filePath, { model_name: newModelName });
|
||||
|
||||
// BUGFIX: Directly update the card's dataset.name attribute to ensure
|
||||
// it's correctly read when reopening the modal
|
||||
@@ -305,7 +305,7 @@ async function saveBaseModel(filePath, originalValue) {
|
||||
await saveModelMetadata(filePath, { base_model: newBaseModel });
|
||||
|
||||
// Update the corresponding lora card's dataset
|
||||
updateLoraCard(filePath, { base_model: newBaseModel });
|
||||
updateModelCard(filePath, { base_model: newBaseModel });
|
||||
|
||||
showToast('Base model updated successfully', 'success');
|
||||
} catch (error) {
|
||||
@@ -434,7 +434,7 @@ export function setupFileNameEditing(filePath) {
|
||||
// Get the new file path and update the card
|
||||
const newFilePath = filePath.replace(originalValue, newFileName);
|
||||
// Pass the new file_name in the updates object for proper card update
|
||||
updateLoraCard(filePath, { file_name: newFileName }, newFilePath);
|
||||
updateModelCard(filePath, { file_name: newFileName, filepath: newFilePath });
|
||||
} else {
|
||||
throw new Error(result.error || 'Unknown error');
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { saveModelMetadata } from '../../api/loraApi.js';
|
||||
import { updateLoraCard } from '../../utils/cardUpdater.js';
|
||||
|
||||
// Preset tag suggestions
|
||||
const PRESET_TAGS = [
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from './ModelMetadata.js';
|
||||
import { saveModelMetadata } from '../../api/loraApi.js';
|
||||
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
|
||||
import { updateLoraCard } from '../../utils/cardUpdater.js';
|
||||
import { updateModelCard } from '../../utils/cardUpdater.js';
|
||||
import { state } from '../../state/index.js';
|
||||
|
||||
/**
|
||||
@@ -270,7 +270,7 @@ window.saveNotes = async function(filePath) {
|
||||
await saveModelMetadata(filePath, { notes: content });
|
||||
|
||||
// Update the corresponding lora card's dataset
|
||||
updateLoraCard(filePath, { notes: content });
|
||||
updateModelCard(filePath, { notes: content });
|
||||
|
||||
showToast('Notes saved successfully', 'success');
|
||||
} catch (error) {
|
||||
@@ -337,7 +337,7 @@ function setupEditableFields(filePath) {
|
||||
});
|
||||
|
||||
// Update the card with the new usage tips
|
||||
updateLoraCard(filePath, { usage_tips: newPresetsJson });
|
||||
updateModelCard(filePath, { usage_tips: newPresetsJson });
|
||||
|
||||
presetTags.innerHTML = renderPresetTags(currentPresets);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { showToast } from '../utils/uiHelpers.js';
|
||||
import { state } from '../state/index.js';
|
||||
import { resetAndReload } from '../api/loraApi.js';
|
||||
import { state, getCurrentPageState } from '../state/index.js';
|
||||
import { modalManager } from './ModalManager.js';
|
||||
import { getStorageItem } from '../utils/storageHelpers.js';
|
||||
import { updateModelCard } from '../utils/cardUpdater.js';
|
||||
|
||||
class MoveManager {
|
||||
constructor() {
|
||||
@@ -136,13 +136,45 @@ class MoveManager {
|
||||
if (this.bulkFilePaths) {
|
||||
// Bulk move mode
|
||||
await this.moveBulkModels(this.bulkFilePaths, targetPath);
|
||||
|
||||
// Update virtual scroller if in active folder view
|
||||
const pageState = getCurrentPageState();
|
||||
if (pageState.activeFolder !== null && state.virtualScroller) {
|
||||
// Remove moved items from virtual scroller instead of reloading
|
||||
this.bulkFilePaths.forEach(filePath => {
|
||||
state.virtualScroller.removeItemByFilePath(filePath);
|
||||
});
|
||||
} else {
|
||||
// Update the model cards' filepath in the DOM
|
||||
this.bulkFilePaths.forEach(filePath => {
|
||||
// Extract filename from original path
|
||||
const filename = filePath.substring(filePath.lastIndexOf('/') + 1);
|
||||
// Construct new filepath
|
||||
const newFilePath = `${targetPath}/${filename}`;
|
||||
// Update the card with new filepath
|
||||
updateModelCard(filePath, {filepath: newFilePath});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Single move mode
|
||||
await this.moveSingleModel(this.currentFilePath, targetPath);
|
||||
|
||||
// Update virtual scroller if in active folder view
|
||||
const pageState = getCurrentPageState();
|
||||
if (pageState.activeFolder !== null && state.virtualScroller) {
|
||||
// Remove moved item from virtual scroller instead of reloading
|
||||
state.virtualScroller.removeItemByFilePath(this.currentFilePath);
|
||||
} else {
|
||||
// Extract filename from original path
|
||||
const filename = this.currentFilePath.substring(this.currentFilePath.lastIndexOf('/') + 1);
|
||||
// Construct new filepath
|
||||
const newFilePath = `${targetPath}/${filename}`;
|
||||
// Update the card with new filepath
|
||||
updateModelCard(this.currentFilePath, {filepath: newFilePath});
|
||||
}
|
||||
}
|
||||
|
||||
modalManager.closeModal('moveModal');
|
||||
await resetAndReload(true);
|
||||
|
||||
// If we were in bulk mode, exit it after successful move
|
||||
if (this.bulkFilePaths && state.bulkMode) {
|
||||
|
||||
@@ -801,11 +801,7 @@ export class VirtualScroller {
|
||||
if (!filePath || this.disabled || this.items.length === 0) return false;
|
||||
|
||||
// Find the index of the item with the matching file path
|
||||
const index = this.items.findIndex(item =>
|
||||
item.file_path === filePath ||
|
||||
item.filepath === filePath ||
|
||||
item.path === filePath
|
||||
);
|
||||
const index = this.items.findIndex(item => item.file_path === filePath);
|
||||
|
||||
if (index === -1) {
|
||||
console.warn(`Item with file path ${filePath} not found in virtual scroller data`);
|
||||
|
||||
@@ -2,129 +2,45 @@
|
||||
* Utility functions to update checkpoint cards after modal edits
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update the checkpoint card after metadata edits in the modal
|
||||
* @param {string} filePath - Path to the checkpoint file
|
||||
* @param {Object} updates - Object containing the updates (model_name, base_model, etc)
|
||||
*/
|
||||
export function updateCheckpointCard(filePath, updates) {
|
||||
// Find the card with matching filepath
|
||||
const checkpointCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
|
||||
if (!checkpointCard) return;
|
||||
|
||||
// Update card dataset and visual elements based on the updates object
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
// Update dataset
|
||||
checkpointCard.dataset[key] = value;
|
||||
|
||||
// Update visual elements based on the property
|
||||
switch(key) {
|
||||
case 'name': // model_name
|
||||
// Update the model name in the footer
|
||||
const modelNameElement = checkpointCard.querySelector('.model-name');
|
||||
if (modelNameElement) modelNameElement.textContent = value;
|
||||
break;
|
||||
|
||||
case 'base_model':
|
||||
// Update the base model label in the card header
|
||||
const baseModelLabel = checkpointCard.querySelector('.base-model-label');
|
||||
if (baseModelLabel) {
|
||||
baseModelLabel.textContent = value;
|
||||
baseModelLabel.title = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'filepath':
|
||||
// The filepath was changed (file renamed), update the dataset
|
||||
checkpointCard.dataset.filepath = value;
|
||||
break;
|
||||
|
||||
case 'tags':
|
||||
// Update tags if they're displayed on the card
|
||||
try {
|
||||
checkpointCard.dataset.tags = JSON.stringify(value);
|
||||
} catch (e) {
|
||||
console.error('Failed to update tags:', e);
|
||||
}
|
||||
break;
|
||||
|
||||
// Add other properties as needed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Lora card after metadata edits in the modal
|
||||
* @param {string} filePath - Path to the Lora file
|
||||
* @param {Object} updates - Object containing the updates (model_name, base_model, notes, usage_tips, etc)
|
||||
* @param {string} [newFilePath] - Optional new file path if the file has been renamed
|
||||
*/
|
||||
export function updateLoraCard(filePath, updates, newFilePath) {
|
||||
export function updateModelCard(filePath, updates) {
|
||||
// Find the card with matching filepath
|
||||
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
|
||||
if (!loraCard) return;
|
||||
|
||||
// If file was renamed, update the filepath first
|
||||
if (newFilePath) {
|
||||
loraCard.dataset.filepath = newFilePath;
|
||||
}
|
||||
const modelCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
|
||||
if (!modelCard) return;
|
||||
|
||||
// Update card dataset and visual elements based on the updates object
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
// Update dataset
|
||||
loraCard.dataset[key] = value;
|
||||
modelCard.dataset[key] = value;
|
||||
|
||||
// Update visual elements based on the property
|
||||
switch(key) {
|
||||
case 'model_name':
|
||||
// Update the model name in the card title
|
||||
const titleElement = loraCard.querySelector('.card-title');
|
||||
const titleElement = modelCard.querySelector('.card-title');
|
||||
if (titleElement) titleElement.textContent = value;
|
||||
|
||||
// Also update the model name in the footer if it exists
|
||||
const modelNameElement = loraCard.querySelector('.model-name');
|
||||
const modelNameElement = modelCard.querySelector('.model-name');
|
||||
if (modelNameElement) modelNameElement.textContent = value;
|
||||
break;
|
||||
|
||||
case 'file_name':
|
||||
// Update the file_name in the dataset
|
||||
loraCard.dataset.file_name = value;
|
||||
break;
|
||||
|
||||
case 'base_model':
|
||||
// Update the base model label in the card header if it exists
|
||||
const baseModelLabel = loraCard.querySelector('.base-model-label');
|
||||
const baseModelLabel = modelCard.querySelector('.base-model-label');
|
||||
if (baseModelLabel) {
|
||||
baseModelLabel.textContent = value;
|
||||
baseModelLabel.title = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tags':
|
||||
// Update tags if they're displayed on the card
|
||||
try {
|
||||
if (typeof value === 'string') {
|
||||
loraCard.dataset.tags = value;
|
||||
} else {
|
||||
loraCard.dataset.tags = JSON.stringify(value);
|
||||
}
|
||||
|
||||
// If there's a tag container, update its content
|
||||
const tagContainer = loraCard.querySelector('.card-tags');
|
||||
if (tagContainer) {
|
||||
// This depends on how your tags are rendered
|
||||
// You may need to update this logic based on your tag rendering function
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to update tags:', e);
|
||||
}
|
||||
break;
|
||||
|
||||
// No visual updates needed for notes, usage_tips as they're typically not shown on cards
|
||||
}
|
||||
});
|
||||
|
||||
return loraCard; // Return the updated card element for chaining
|
||||
return modelCard; // Return the updated card element for chaining
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user