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 @@
+
+
+
Missing LoRAs to Download
+
+ Total download size: Calculating...
+
+
+
+
+
+