// Recipe Modal Component import { showToast } from '../utils/uiHelpers.js'; class RecipeModal { constructor() { this.init(); } init() { this.setupCopyButtons(); } showRecipeDetails(recipe) { // Set modal title const modalTitle = document.getElementById('recipeModalTitle'); if (modalTitle) { modalTitle.textContent = recipe.title || 'Recipe Details'; } // Set recipe tags if they exist const tagsCompactElement = document.getElementById('recipeTagsCompact'); const tagsTooltipContent = document.getElementById('recipeTagsTooltipContent'); if (tagsCompactElement && tagsTooltipContent && recipe.tags && recipe.tags.length > 0) { // Clear previous tags tagsCompactElement.innerHTML = ''; tagsTooltipContent.innerHTML = ''; // Limit displayed tags to 5, show a "+X more" button if needed const maxVisibleTags = 5; const visibleTags = recipe.tags.slice(0, maxVisibleTags); const remainingTags = recipe.tags.length > maxVisibleTags ? recipe.tags.slice(maxVisibleTags) : []; // Add visible tags visibleTags.forEach(tag => { const tagElement = document.createElement('div'); tagElement.className = 'recipe-tag-compact'; tagElement.textContent = tag; tagsCompactElement.appendChild(tagElement); }); // Add "more" button if needed if (remainingTags.length > 0) { const moreButton = document.createElement('div'); moreButton.className = 'recipe-tag-more'; moreButton.textContent = `+${remainingTags.length} more`; tagsCompactElement.appendChild(moreButton); // Add tooltip functionality moreButton.addEventListener('mouseenter', () => { document.getElementById('recipeTagsTooltip').classList.add('visible'); }); moreButton.addEventListener('mouseleave', () => { setTimeout(() => { if (!document.getElementById('recipeTagsTooltip').matches(':hover')) { document.getElementById('recipeTagsTooltip').classList.remove('visible'); } }, 300); }); document.getElementById('recipeTagsTooltip').addEventListener('mouseleave', () => { document.getElementById('recipeTagsTooltip').classList.remove('visible'); }); // Add all tags to tooltip recipe.tags.forEach(tag => { const tooltipTag = document.createElement('div'); tooltipTag.className = 'tooltip-tag'; tooltipTag.textContent = tag; tagsTooltipContent.appendChild(tooltipTag); }); } } else if (tagsCompactElement) { // No tags to display tagsCompactElement.innerHTML = ''; } // Set recipe image const modalImage = document.getElementById('recipeModalImage'); if (modalImage) { // Ensure file_url exists, fallback to file_path if needed const imageUrl = recipe.file_url || (recipe.file_path ? `/loras_static/root1/preview/${recipe.file_path.split('/').pop()}` : '/loras_static/images/no-preview.png'); modalImage.src = imageUrl; modalImage.alt = recipe.title || 'Recipe Preview'; } // Set generation parameters const promptElement = document.getElementById('recipePrompt'); const negativePromptElement = document.getElementById('recipeNegativePrompt'); const otherParamsElement = document.getElementById('recipeOtherParams'); if (recipe.gen_params) { // Set prompt if (promptElement && recipe.gen_params.prompt) { promptElement.textContent = recipe.gen_params.prompt; } else if (promptElement) { promptElement.textContent = 'No prompt information available'; } // Set negative prompt if (negativePromptElement && recipe.gen_params.negative_prompt) { negativePromptElement.textContent = recipe.gen_params.negative_prompt; } else if (negativePromptElement) { negativePromptElement.textContent = 'No negative prompt information available'; } // Set other parameters if (otherParamsElement) { // Clear previous params otherParamsElement.innerHTML = ''; // Add all other parameters except prompt and negative_prompt const excludedParams = ['prompt', 'negative_prompt']; for (const [key, value] of Object.entries(recipe.gen_params)) { if (!excludedParams.includes(key) && value !== undefined && value !== null) { const paramTag = document.createElement('div'); paramTag.className = 'param-tag'; paramTag.innerHTML = ` ${key}: ${value} `; otherParamsElement.appendChild(paramTag); } } // If no other params, show a message if (otherParamsElement.children.length === 0) { otherParamsElement.innerHTML = '
No additional parameters available
'; } } } else { // No generation parameters available if (promptElement) promptElement.textContent = 'No prompt information available'; if (negativePromptElement) negativePromptElement.textContent = 'No negative prompt information available'; if (otherParamsElement) otherParamsElement.innerHTML = '
No parameters available
'; } // Set LoRAs list and count const lorasListElement = document.getElementById('recipeLorasList'); const lorasCountElement = document.getElementById('recipeLorasCount'); // 检查所有 LoRAs 是否都在库中 let allLorasAvailable = true; let missingLorasCount = 0; if (recipe.loras && recipe.loras.length > 0) { recipe.loras.forEach(lora => { if (!lora.inLibrary) { allLorasAvailable = false; missingLorasCount++; } }); } // 设置 LoRAs 计数和状态 if (lorasCountElement && recipe.loras) { const totalCount = recipe.loras.length; // 创建状态指示器 let statusHTML = ''; if (totalCount > 0) { if (allLorasAvailable) { statusHTML = `
Ready to use
`; } else { statusHTML = `
${missingLorasCount} missing
`; } } lorasCountElement.innerHTML = ` ${totalCount} LoRAs ${statusHTML}`; } if (lorasListElement && recipe.loras && recipe.loras.length > 0) { lorasListElement.innerHTML = recipe.loras.map(lora => { const existsLocally = lora.inLibrary; const localPath = lora.localPath || ''; // Create local status badge const localStatus = existsLocally ? `
In Library
${localPath}
` : `
Not in Library
`; return `
LoRA preview

${lora.modelName}

${localStatus}
${lora.modelVersionName ? `
${lora.modelVersionName}
` : ''}
Weight: ${lora.strength || 1.0}
${lora.baseModel ? `
${lora.baseModel}
` : ''}
`; }).join(''); // Generate recipe syntax for copy button this.recipeLorasSyntax = recipe.loras.map(lora => `` ).join(' '); } else if (lorasListElement) { lorasListElement.innerHTML = '
No LoRAs associated with this recipe
'; this.recipeLorasSyntax = ''; } // Show the modal modalManager.showModal('recipeModal'); } // Setup copy buttons for prompts and recipe syntax setupCopyButtons() { const copyPromptBtn = document.getElementById('copyPromptBtn'); const copyNegativePromptBtn = document.getElementById('copyNegativePromptBtn'); const copyRecipeSyntaxBtn = document.getElementById('copyRecipeSyntaxBtn'); if (copyPromptBtn) { copyPromptBtn.addEventListener('click', () => { const promptText = document.getElementById('recipePrompt').textContent; this.copyToClipboard(promptText, 'Prompt copied to clipboard'); }); } if (copyNegativePromptBtn) { copyNegativePromptBtn.addEventListener('click', () => { const negativePromptText = document.getElementById('recipeNegativePrompt').textContent; this.copyToClipboard(negativePromptText, 'Negative prompt copied to clipboard'); }); } if (copyRecipeSyntaxBtn) { copyRecipeSyntaxBtn.addEventListener('click', () => { this.copyToClipboard(this.recipeLorasSyntax, 'Recipe syntax copied to clipboard'); }); } } // Helper method to copy text to clipboard copyToClipboard(text, successMessage) { navigator.clipboard.writeText(text).then(() => { showToast(successMessage, 'success'); }).catch(err => { console.error('Failed to copy text: ', err); showToast('Failed to copy text', 'error'); }); } } export { RecipeModal };