mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-23 22:22:11 -03:00
feat: Implement model move, import, and download functionalities with corresponding UI and API updates.
This commit is contained in:
@@ -28,7 +28,7 @@ export class ImportManager {
|
||||
this.importMode = 'url'; // Default mode: 'url' or 'upload'
|
||||
this.useDefaultPath = false;
|
||||
this.apiClient = null;
|
||||
|
||||
|
||||
// Initialize sub-managers
|
||||
this.loadingManager = new LoadingManager();
|
||||
this.stepManager = new ImportStepManager();
|
||||
@@ -36,7 +36,7 @@ export class ImportManager {
|
||||
this.recipeDataManager = new RecipeDataManager(this);
|
||||
this.downloadManager = new DownloadManager(this);
|
||||
this.folderTreeManager = new FolderTreeManager();
|
||||
|
||||
|
||||
// Bind methods
|
||||
this.formatFileSize = formatFileSize;
|
||||
this.updateTargetPath = this.updateTargetPath.bind(this);
|
||||
@@ -53,17 +53,17 @@ export class ImportManager {
|
||||
this.initializeEventHandlers();
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
||||
// Get API client for LoRAs
|
||||
this.apiClient = getModelApiClient(MODEL_TYPES.LORA);
|
||||
|
||||
|
||||
// Reset state
|
||||
this.resetSteps();
|
||||
if (recipeData) {
|
||||
this.downloadableLoRAs = recipeData.loras;
|
||||
this.recipeId = recipeId;
|
||||
}
|
||||
|
||||
|
||||
// Show modal
|
||||
modalManager.showModal('importModal', null, () => {
|
||||
this.cleanupFolderBrowser();
|
||||
@@ -71,7 +71,7 @@ export class ImportManager {
|
||||
});
|
||||
|
||||
// Verify visibility and focus on URL input
|
||||
setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
// Ensure URL option is selected and focus on the input
|
||||
this.toggleImportMode('url');
|
||||
const urlInput = document.getElementById('imageUrlInput');
|
||||
@@ -93,32 +93,32 @@ export class ImportManager {
|
||||
// Clear UI state
|
||||
this.stepManager.removeInjectedStyles();
|
||||
this.stepManager.showStep('uploadStep');
|
||||
|
||||
|
||||
// Reset form inputs
|
||||
const fileInput = document.getElementById('recipeImageUpload');
|
||||
if (fileInput) fileInput.value = '';
|
||||
|
||||
|
||||
const urlInput = document.getElementById('imageUrlInput');
|
||||
if (urlInput) urlInput.value = '';
|
||||
|
||||
|
||||
const uploadError = document.getElementById('uploadError');
|
||||
if (uploadError) uploadError.textContent = '';
|
||||
|
||||
|
||||
const importUrlError = document.getElementById('importUrlError');
|
||||
if (importUrlError) importUrlError.textContent = '';
|
||||
|
||||
|
||||
const recipeName = document.getElementById('recipeName');
|
||||
if (recipeName) recipeName.value = '';
|
||||
|
||||
|
||||
const tagsContainer = document.getElementById('tagsContainer');
|
||||
if (tagsContainer) tagsContainer.innerHTML = `<div class="empty-tags">${translate('recipes.controls.import.noTagsAdded', {}, 'No tags added')}</div>`;
|
||||
|
||||
|
||||
// Clear folder path input
|
||||
const folderPathInput = document.getElementById('importFolderPath');
|
||||
if (folderPathInput) {
|
||||
folderPathInput.value = '';
|
||||
}
|
||||
|
||||
|
||||
// Reset state variables
|
||||
this.recipeImage = null;
|
||||
this.recipeData = null;
|
||||
@@ -127,30 +127,30 @@ export class ImportManager {
|
||||
this.missingLoras = [];
|
||||
this.downloadableLoRAs = [];
|
||||
this.selectedFolder = '';
|
||||
|
||||
|
||||
// Reset import mode
|
||||
this.importMode = 'url';
|
||||
this.toggleImportMode('url');
|
||||
|
||||
|
||||
// Clear folder tree selection
|
||||
if (this.folderTreeManager) {
|
||||
this.folderTreeManager.clearSelection();
|
||||
}
|
||||
|
||||
|
||||
// Reset default path toggle
|
||||
this.loadDefaultPathSetting();
|
||||
|
||||
|
||||
// Reset duplicate related properties
|
||||
this.duplicateRecipes = [];
|
||||
}
|
||||
|
||||
toggleImportMode(mode) {
|
||||
this.importMode = mode;
|
||||
|
||||
|
||||
// Update toggle buttons
|
||||
const uploadBtn = document.querySelector('.toggle-btn[data-mode="upload"]');
|
||||
const urlBtn = document.querySelector('.toggle-btn[data-mode="url"]');
|
||||
|
||||
|
||||
if (uploadBtn && urlBtn) {
|
||||
if (mode === 'upload') {
|
||||
uploadBtn.classList.add('active');
|
||||
@@ -160,11 +160,11 @@ export class ImportManager {
|
||||
urlBtn.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Show/hide appropriate sections
|
||||
const uploadSection = document.getElementById('uploadSection');
|
||||
const urlSection = document.getElementById('urlSection');
|
||||
|
||||
|
||||
if (uploadSection && urlSection) {
|
||||
if (mode === 'upload') {
|
||||
uploadSection.style.display = 'block';
|
||||
@@ -174,11 +174,11 @@ export class ImportManager {
|
||||
urlSection.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clear error messages
|
||||
const uploadError = document.getElementById('uploadError');
|
||||
const importUrlError = document.getElementById('importUrlError');
|
||||
|
||||
|
||||
if (uploadError) uploadError.textContent = '';
|
||||
if (importUrlError) importUrlError.textContent = '';
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export class ImportManager {
|
||||
addTag() {
|
||||
this.recipeDataManager.addTag();
|
||||
}
|
||||
|
||||
|
||||
removeTag(tag) {
|
||||
this.recipeDataManager.removeTag(tag);
|
||||
}
|
||||
@@ -217,12 +217,12 @@ export class ImportManager {
|
||||
|
||||
async proceedToLocation() {
|
||||
this.stepManager.showStep('locationStep');
|
||||
|
||||
|
||||
try {
|
||||
// Fetch LoRA roots
|
||||
const rootsData = await this.apiClient.fetchModelRoots();
|
||||
const loraRoot = document.getElementById('importLoraRoot');
|
||||
loraRoot.innerHTML = rootsData.roots.map(root =>
|
||||
loraRoot.innerHTML = rootsData.roots.map(root =>
|
||||
`<option value="${root}">${root}</option>`
|
||||
).join('');
|
||||
|
||||
@@ -247,19 +247,19 @@ export class ImportManager {
|
||||
this.updateTargetPath();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Initialize folder tree
|
||||
await this.initializeFolderTree();
|
||||
|
||||
|
||||
// Setup lora root change handler
|
||||
loraRoot.addEventListener('change', async () => {
|
||||
await this.initializeFolderTree();
|
||||
this.updateTargetPath();
|
||||
});
|
||||
|
||||
|
||||
// Load default path setting for LoRAs
|
||||
this.loadDefaultPathSetting();
|
||||
|
||||
|
||||
this.updateTargetPath();
|
||||
} catch (error) {
|
||||
showToast('toast.recipes.importFailed', { message: error.message }, 'error');
|
||||
@@ -268,19 +268,19 @@ export class ImportManager {
|
||||
|
||||
backToUpload() {
|
||||
this.stepManager.showStep('uploadStep');
|
||||
|
||||
|
||||
// Reset file input
|
||||
const fileInput = document.getElementById('recipeImageUpload');
|
||||
if (fileInput) fileInput.value = '';
|
||||
|
||||
|
||||
// Reset URL input
|
||||
const urlInput = document.getElementById('imageUrlInput');
|
||||
if (urlInput) urlInput.value = '';
|
||||
|
||||
|
||||
// Clear error messages
|
||||
const uploadError = document.getElementById('uploadError');
|
||||
if (uploadError) uploadError.textContent = '';
|
||||
|
||||
|
||||
const importUrlError = document.getElementById('importUrlError');
|
||||
if (importUrlError) importUrlError.textContent = '';
|
||||
}
|
||||
@@ -296,7 +296,7 @@ export class ImportManager {
|
||||
loadDefaultPathSetting() {
|
||||
const storageKey = 'use_default_path_loras';
|
||||
this.useDefaultPath = getStorageItem(storageKey, false);
|
||||
|
||||
|
||||
const toggleInput = document.getElementById('importUseDefaultPath');
|
||||
if (toggleInput) {
|
||||
toggleInput.checked = this.useDefaultPath;
|
||||
@@ -306,18 +306,18 @@ export class ImportManager {
|
||||
|
||||
toggleDefaultPath(event) {
|
||||
this.useDefaultPath = event.target.checked;
|
||||
|
||||
|
||||
// Save to localStorage for LoRAs
|
||||
const storageKey = 'use_default_path_loras';
|
||||
setStorageItem(storageKey, this.useDefaultPath);
|
||||
|
||||
|
||||
this.updatePathSelectionUI();
|
||||
this.updateTargetPath();
|
||||
}
|
||||
|
||||
updatePathSelectionUI() {
|
||||
const manualSelection = document.getElementById('importManualPathSelection');
|
||||
|
||||
|
||||
// Always show manual path selection, but disable/enable based on useDefaultPath
|
||||
if (manualSelection) {
|
||||
manualSelection.style.display = 'block';
|
||||
@@ -336,7 +336,7 @@ export class ImportManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Always update the main path display
|
||||
this.updateTargetPath();
|
||||
}
|
||||
@@ -345,7 +345,7 @@ export class ImportManager {
|
||||
try {
|
||||
// Fetch unified folder tree
|
||||
const treeData = await this.apiClient.fetchUnifiedFolderTree();
|
||||
|
||||
|
||||
if (treeData.success) {
|
||||
// Load tree data into folder tree manager
|
||||
await this.folderTreeManager.loadTree(treeData.tree);
|
||||
@@ -368,8 +368,8 @@ export class ImportManager {
|
||||
updateTargetPath() {
|
||||
const pathDisplay = document.getElementById('importTargetPathDisplay');
|
||||
const loraRoot = document.getElementById('importLoraRoot').value;
|
||||
|
||||
let fullPath = loraRoot || translate('recipes.controls.import.selectLoraRoot', {}, 'Select a LoRA root directory'); if (loraRoot) {
|
||||
|
||||
let fullPath = loraRoot || translate('recipes.controls.import.selectLoraRoot', {}, 'Select a LoRA root directory'); if (loraRoot) {
|
||||
if (this.useDefaultPath) {
|
||||
// Show actual template path
|
||||
try {
|
||||
@@ -417,19 +417,19 @@ export class ImportManager {
|
||||
// Store the recipe data and ID
|
||||
this.recipeData = recipeData;
|
||||
this.recipeId = recipeId;
|
||||
|
||||
|
||||
// Show the modal and go to location step
|
||||
this.showImportModal(recipeData, recipeId);
|
||||
this.proceedToLocation();
|
||||
|
||||
|
||||
// Update the modal title
|
||||
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');
|
||||
|
||||
|
||||
// Hide the back button
|
||||
const backButton = document.querySelector('#locationStep .secondary-btn');
|
||||
if (backButton) backButton.style.display = 'none';
|
||||
|
||||
Reference in New Issue
Block a user