/** * ShowcaseView.js * Handles showcase content (images, videos) display for checkpoint modal */ import { showToast, copyToClipboard, getLocalExampleImageUrl, initLazyLoading, initNsfwBlurHandlers, initMetadataPanelHandlers, toggleShowcase, setupShowcaseScroll, scrollToTop } from '../../utils/uiHelpers.js'; import { state } from '../../state/index.js'; import { NSFW_LEVELS } from '../../utils/constants.js'; /** * Render showcase content * @param {Array} images - Array of images/videos to show * @param {string} modelHash - Model hash for identifying local files * @returns {string} HTML content */ export function renderShowcaseContent(images, modelHash) { if (!images?.length) return '
No example images available
'; // Filter images based on SFW setting const showOnlySFW = state.settings.show_only_sfw; let filteredImages = images; let hiddenCount = 0; if (showOnlySFW) { filteredImages = images.filter(img => { const nsfwLevel = img.nsfwLevel !== undefined ? img.nsfwLevel : 0; const isSfw = nsfwLevel < NSFW_LEVELS.R; if (!isSfw) hiddenCount++; return isSfw; }); } // Show message if no images are available after filtering if (filteredImages.length === 0) { return `

All example images are filtered due to NSFW content settings

Your settings are currently set to show only safe-for-work content

You can change this in Settings

`; } // Show hidden content notification if applicable const hiddenNotification = hiddenCount > 0 ? `
${hiddenCount} ${hiddenCount === 1 ? 'image' : 'images'} hidden due to SFW-only setting
` : ''; return `
Scroll or click to show ${filteredImages.length} examples
`; } /** * Generate media wrapper HTML for an image or video * @param {Object} media - Media object with image or video data * @returns {string} HTML content */ function generateMediaWrapper(media, urls) { // Calculate appropriate aspect ratio const aspectRatio = (media.height / media.width) * 100; const containerWidth = 800; // modal content maximum width const minHeightPercent = 40; const maxHeightPercent = (window.innerHeight * 0.6 / containerWidth) * 100; const heightPercent = Math.max( minHeightPercent, Math.min(maxHeightPercent, aspectRatio) ); // Check if media should be blurred const nsfwLevel = media.nsfwLevel !== undefined ? media.nsfwLevel : 0; const shouldBlur = state.settings.blurMatureContent && nsfwLevel > NSFW_LEVELS.PG13; // Determine NSFW warning text based on level let nsfwText = "Mature Content"; if (nsfwLevel >= NSFW_LEVELS.XXX) { nsfwText = "XXX-rated Content"; } else if (nsfwLevel >= NSFW_LEVELS.X) { nsfwText = "X-rated Content"; } else if (nsfwLevel >= NSFW_LEVELS.R) { nsfwText = "R-rated Content"; } // Extract metadata from the media const meta = media.meta || {}; const prompt = meta.prompt || ''; const negativePrompt = meta.negative_prompt || meta.negativePrompt || ''; const size = meta.Size || `${media.width}x${media.height}`; const seed = meta.seed || ''; const model = meta.Model || ''; const steps = meta.steps || ''; const sampler = meta.sampler || ''; const cfgScale = meta.cfgScale || ''; const clipSkip = meta.clipSkip || ''; // Check if we have any meaningful generation parameters const hasParams = seed || model || steps || sampler || cfgScale || clipSkip; const hasPrompts = prompt || negativePrompt; // Create metadata panel content const metadataPanel = generateMetadataPanel( hasParams, hasPrompts, prompt, negativePrompt, size, seed, model, steps, sampler, cfgScale, clipSkip ); // Check if this is a video or image if (media.type === 'video') { return generateVideoWrapper(media, heightPercent, shouldBlur, nsfwText, metadataPanel, urls); } return generateImageWrapper(media, heightPercent, shouldBlur, nsfwText, metadataPanel, urls); } /** * Generate metadata panel HTML */ function generateMetadataPanel(hasParams, hasPrompts, prompt, negativePrompt, size, seed, model, steps, sampler, cfgScale, clipSkip) { // Create unique IDs for prompt copying const promptIndex = Math.random().toString(36).substring(2, 15); const negPromptIndex = Math.random().toString(36).substring(2, 15); let content = '
'; if (hasParams) { content += `
${size ? `
Size:${size}
` : ''} ${seed ? `
Seed:${seed}
` : ''} ${model ? `
Model:${model}
` : ''} ${steps ? `
Steps:${steps}
` : ''} ${sampler ? `
Sampler:${sampler}
` : ''} ${cfgScale ? `
CFG:${cfgScale}
` : ''} ${clipSkip ? `
Clip Skip:${clipSkip}
` : ''}
`; } if (!hasParams && !hasPrompts) { content += ` `; } if (prompt) { content += ` `; } if (negativePrompt) { content += ` `; } content += '
'; return content; } /** * Generate video wrapper HTML */ function generateVideoWrapper(media, heightPercent, shouldBlur, nsfwText, metadataPanel, urls) { return `
${shouldBlur ? ` ` : ''} ${shouldBlur ? `

${nsfwText}

` : ''} ${metadataPanel}
`; } /** * Generate image wrapper HTML */ function generateImageWrapper(media, heightPercent, shouldBlur, nsfwText, metadataPanel, urls) { return `
${shouldBlur ? ` ` : ''} Preview ${shouldBlur ? `

${nsfwText}

` : ''} ${metadataPanel}
`; } // Use the shared setupShowcaseScroll function with the correct modal ID export { setupShowcaseScroll, scrollToTop, toggleShowcase }; // Initialize the showcase scroll when this module is imported document.addEventListener('DOMContentLoaded', () => { setupShowcaseScroll('checkpointModal'); });