diff --git a/py/routes/recipe_routes.py b/py/routes/recipe_routes.py index 234a1ed5..8325440c 100644 --- a/py/routes/recipe_routes.py +++ b/py/routes/recipe_routes.py @@ -220,12 +220,19 @@ class RecipeRoutes: # Check if this LoRA exists locally by SHA256 hash exists_locally = False + local_path = None - if civitai_info and 'files' in civitai_info and civitai_info['files']: - sha256 = civitai_info['files'][0].get('hashes', {}).get('SHA256', '') - if sha256: - sha256 = sha256.lower() # Convert to lowercase for consistency - exists_locally = self.recipe_scanner._lora_scanner.has_lora_hash(sha256) + if civitai_info and 'files' in civitai_info: + # Find the model file (type="Model") in the files list + model_file = next((file for file in civitai_info.get('files', []) + if file.get('type') == 'Model'), None) + + if model_file: + sha256 = model_file.get('hashes', {}).get('SHA256', '') + if sha256: + exists_locally = self.recipe_scanner._lora_scanner.has_lora_hash(sha256) + if exists_locally: + local_path = self.recipe_scanner._lora_scanner.get_lora_path_by_hash(sha256) # Create LoRA entry lora_entry = { @@ -235,6 +242,7 @@ class RecipeRoutes: 'type': resource.get('type', 'lora'), 'weight': resource.get('weight', 1.0), 'existsLocally': exists_locally, + 'localPath': local_path, 'thumbnailUrl': '', 'baseModel': '', 'size': 0, @@ -250,9 +258,9 @@ class RecipeRoutes: # Get base model lora_entry['baseModel'] = civitai_info.get('baseModel', '') - # Get file size - if 'files' in civitai_info and civitai_info['files']: - lora_entry['size'] = civitai_info['files'][0].get('sizeKB', 0) * 1024 + # Get file size from model file + if model_file: + lora_entry['size'] = model_file.get('sizeKB', 0) * 1024 # Get download URL lora_entry['downloadUrl'] = civitai_info.get('downloadUrl', '') diff --git a/static/css/components/import-modal.css b/static/css/components/import-modal.css index 5ce2a0a5..a83e5c08 100644 --- a/static/css/components/import-modal.css +++ b/static/css/components/import-modal.css @@ -1,6 +1,7 @@ /* Import Modal Styles */ .import-step { margin: var(--space-2) 0; + transition: none !important; /* Disable any transitions that might affect display */ } /* File Input Styles */ @@ -364,14 +365,6 @@ margin-top: 4px; } -/* Modal Actions */ -.modal-actions { - display: flex; - justify-content: flex-end; - gap: var(--space-2); - margin-top: var(--space-3); -} - /* Dark theme adjustments */ [data-theme="dark"] .lora-item { background: var(--lora-surface); @@ -391,3 +384,58 @@ height: 150px; } } + +/* Size badge for LoRA items */ +.size-badge { + background: var(--lora-surface); + padding: 2px 8px; + border-radius: var(--border-radius-xs); + font-size: 0.85em; + color: var(--text-color); + opacity: 0.8; +} + +/* Missing LoRAs summary section */ +.missing-loras-summary { + margin-bottom: var(--space-3); + padding: var(--space-2); + background: var(--bg-color); + border-radius: var(--border-radius-sm); + border: 1px solid var(--lora-error); +} + +.missing-loras-summary h3 { + margin-top: 0; + margin-bottom: var(--space-2); + font-size: 1.1em; + color: var(--text-color); +} + +.total-size-info { + margin-bottom: var(--space-2); + font-size: 0.9em; + color: var(--text-color); +} + +.missing-lora-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; + border-bottom: 1px solid var(--border-color); +} + +.missing-lora-item:last-child { + border-bottom: none; +} + +.missing-lora-name { + font-weight: 500; + flex: 1; +} + +.missing-lora-size { + font-size: 0.9em; + color: var(--text-color); + opacity: 0.8; +} \ No newline at end of file diff --git a/static/js/managers/ImportManager.js b/static/js/managers/ImportManager.js index 065acf9d..bee42dbd 100644 --- a/static/js/managers/ImportManager.js +++ b/static/js/managers/ImportManager.js @@ -36,14 +36,22 @@ export class ImportManager { this.initialized = true; } + // Always reset the state when opening the modal + this.resetSteps(); + + // Show the modal modalManager.showModal('importModal', null, () => { // Cleanup handler when modal closes this.cleanupFolderBrowser(); - // 移除任何强制样式 + // Remove any injected styles this.removeInjectedStyles(); }); - this.resetSteps(); + + // Verify the modal is properly shown + setTimeout(() => { + this.ensureModalVisible(); + }, 50); } // 添加移除注入样式的方法 @@ -52,12 +60,18 @@ export class ImportManager { this.injectedStyles.parentNode.removeChild(this.injectedStyles); this.injectedStyles = null; } + + // Also reset any inline styles that might have been set with !important + document.querySelectorAll('.import-step').forEach(step => { + step.style.cssText = ''; + }); } resetSteps() { - // 移除可能存在的强制样式 + // Remove any existing injected styles this.removeInjectedStyles(); + // Show the first step this.showStep('uploadStep'); // Reset file input @@ -78,6 +92,18 @@ export class ImportManager { previewElement.innerHTML = '
Image preview will appear here
'; } + // Reset recipe name input + const recipeName = document.getElementById('recipeName'); + if (recipeName) { + recipeName.value = ''; + } + + // Reset tags container + const tagsContainer = document.getElementById('tagsContainer'); + if (tagsContainer) { + tagsContainer.innerHTML = '
No tags added
'; + } + // Reset state variables this.recipeImage = null; this.recipeData = null; @@ -92,6 +118,18 @@ export class ImportManager { folderBrowser.querySelectorAll('.folder-item').forEach(f => f.classList.remove('selected')); } + + // Clear missing LoRAs list if it exists + const missingLorasList = document.getElementById('missingLorasList'); + if (missingLorasList) { + missingLorasList.innerHTML = ''; + } + + // Reset total download size + const totalSizeDisplay = document.getElementById('totalDownloadSize'); + if (totalSizeDisplay) { + totalSizeDisplay.textContent = 'Calculating...'; + } } handleImageUpload(event) { @@ -207,6 +245,10 @@ export class ImportManager { Not in Library `; + // Format size if available + const sizeDisplay = lora.size ? + `
${this.formatFileSize(lora.size)}
` : ''; + return `
@@ -220,6 +262,7 @@ export class ImportManager { ${lora.version ? `
${lora.version}
` : ''}
${lora.baseModel ? `
${lora.baseModel}
` : ''} + ${sizeDisplay}
Weight: ${lora.weight || 1.0}
@@ -291,8 +334,6 @@ export class ImportManager { return; } - console.log('Proceeding from details, missing LoRAs:', this.missingLoras.length); - // If we have missing LoRAs, go to location step if (this.missingLoras.length > 0) { this.proceedToLocation(); @@ -303,30 +344,61 @@ export class ImportManager { } async proceedToLocation() { - // 先移除可能已有的样式 - this.removeInjectedStyles(); - - // 添加强制CSS覆盖 - this.injectedStyles = document.createElement('style'); - this.injectedStyles.innerHTML = ` - #locationStep { - display: block !important; - opacity: 1 !important; - visibility: visible !important; - position: static !important; - z-index: 10000 !important; - width: auto !important; - height: auto !important; - overflow: visible !important; - transform: none !important; - } - `; - document.head.appendChild(this.injectedStyles); - console.log('Added override CSS to force visibility'); + // Show the location step with special handling this.showStep('locationStep'); + // Double-check after a short delay to ensure the step is visible + setTimeout(() => { + const locationStep = document.getElementById('locationStep'); + if (locationStep.style.display !== 'block' || + window.getComputedStyle(locationStep).display !== 'block') { + // Force display again + locationStep.style.display = 'block'; + + // If still not visible, try with injected style + if (window.getComputedStyle(locationStep).display !== 'block') { + this.injectedStyles = document.createElement('style'); + this.injectedStyles.innerHTML = ` + #locationStep { + display: block !important; + opacity: 1 !important; + visibility: visible !important; + } + `; + document.head.appendChild(this.injectedStyles); + } + } + }, 100); + try { + // Display missing LoRAs that will be downloaded + const missingLorasList = document.getElementById('missingLorasList'); + if (missingLorasList && this.missingLoras.length > 0) { + // Calculate total size + const totalSize = this.missingLoras.reduce((sum, lora) => { + return sum + (lora.size ? parseInt(lora.size) : 0); + }, 0); + + // Update total size display + const totalSizeDisplay = document.getElementById('totalDownloadSize'); + if (totalSizeDisplay) { + totalSizeDisplay.textContent = this.formatFileSize(totalSize); + } + + // Generate missing LoRAs list + missingLorasList.innerHTML = this.missingLoras.map(lora => { + const sizeDisplay = lora.size ? this.formatFileSize(lora.size) : 'Unknown size'; + + return ` +
+
${lora.name}
+
${sizeDisplay}
+
+ `; + }).join(''); + } + // Fetch LoRA roots const rootsResponse = await fetch('/api/lora-roots'); if (!rootsResponse.ok) { @@ -365,6 +437,18 @@ export class ImportManager { backToUpload() { this.showStep('uploadStep'); + + // Reset file input to ensure it can trigger change events again + const fileInput = document.getElementById('recipeImageUpload'); + if (fileInput) { + fileInput.value = ''; + } + + // Clear any previous error messages + const errorElement = document.getElementById('uploadError'); + if (errorElement) { + errorElement.textContent = ''; + } } backToDetails() { @@ -397,7 +481,6 @@ export class ImportManager { const result = await response.json(); if (result.success) { // Handle successful save - console.log(`Recipe saved with ID: ${result.recipe_id}`); // Show success message for recipe save showToast(`Recipe "${this.recipeName}" saved successfully`, 'success'); @@ -544,7 +627,6 @@ export class ImportManager { if (loraRoot) loraRoot.addEventListener('change', this.updateTargetPath); if (newFolder) newFolder.addEventListener('input', this.updateTargetPath); - console.log('Initializing folder browser...'); // Update initial path this.updateTargetPath(); } @@ -588,36 +670,80 @@ export class ImportManager { } showStep(stepId) { - // 隐藏所有步骤 + + // First, remove any injected styles to prevent conflicts + this.removeInjectedStyles(); + + // Hide all steps first document.querySelectorAll('.import-step').forEach(step => { step.style.display = 'none'; }); - // 显示目标步骤 + // Show target step with a monitoring mechanism const targetStep = document.getElementById(stepId); if (targetStep) { - // 强制显示目标步骤 - 使用 !important 覆盖任何其他CSS规则 - targetStep.setAttribute('style', 'display: block !important'); + // Use direct style setting + targetStep.style.display = 'block'; - // 调试信息 - console.log(`Showing step: ${stepId}`); - const rect = targetStep.getBoundingClientRect(); - console.log('Step dimensions:', { - width: rect.width, - height: rect.height, - top: rect.top, - left: rect.left, - visible: rect.width > 0 && rect.height > 0 - }); + // For the locationStep specifically, we need additional measures + if (stepId === 'locationStep') { + // Create a more persistent style to override any potential conflicts + this.injectedStyles = document.createElement('style'); + this.injectedStyles.innerHTML = ` + #locationStep { + display: block !important; + opacity: 1 !important; + visibility: visible !important; + } + `; + document.head.appendChild(this.injectedStyles); + + // Force layout recalculation + targetStep.offsetHeight; + + // Set up a monitor to ensure the step remains visible + setTimeout(() => { + if (targetStep.style.display !== 'block') { + targetStep.style.display = 'block'; + } + + // Check dimensions again after a short delay + const newRect = targetStep.getBoundingClientRect(); + }, 50); + } - // 强制重新计算布局 - targetStep.offsetHeight; - - // 滚动模态内容到顶部 + // Scroll modal content to top const modalContent = document.querySelector('#importModal .modal-content'); if (modalContent) { modalContent.scrollTop = 0; } } } + + // Add a helper method to format file sizes + formatFileSize(bytes) { + if (!bytes || isNaN(bytes)) return ''; + + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i]; + } + + // Add this method to ensure the modal is fully visible and initialized + ensureModalVisible() { + const importModal = document.getElementById('importModal'); + if (!importModal) { + console.error('Import modal element not found'); + return false; + } + + // Check if modal is actually visible + const modalDisplay = window.getComputedStyle(importModal).display; + if (modalDisplay !== 'block') { + console.error('Import modal is not visible, display: ' + modalDisplay); + return false; + } + + return true; + } } \ No newline at end of file diff --git a/templates/components/import_modal.html b/templates/components/import_modal.html index 1151f7d3..3fb2db06 100644 --- a/templates/components/import_modal.html +++ b/templates/components/import_modal.html @@ -68,6 +68,17 @@