/** * CheckpointModal - Main entry point * * Modularized checkpoint modal component that handles checkpoint model details display */ import { showToast, getExampleImageFiles, initLazyLoading, initNsfwBlurHandlers, initMetadataPanelHandlers } from '../../utils/uiHelpers.js'; import { modalManager } from '../../managers/ModalManager.js'; import { renderShowcaseContent, toggleShowcase, setupShowcaseScroll, scrollToTop } from './ShowcaseView.js'; import { setupTabSwitching, loadModelDescription } from './ModelDescription.js'; import { setupModelNameEditing, setupBaseModelEditing, setupFileNameEditing } from './ModelMetadata.js'; import { saveModelMetadata } from '../../api/checkpointApi.js'; import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js'; import { updateCheckpointCard } from '../../utils/cardUpdater.js'; /** * Display the checkpoint modal with the given checkpoint data * @param {Object} checkpoint - Checkpoint data object */ export function showCheckpointModal(checkpoint) { const content = `
`; modalManager.showModal('checkpointModal', content); setupEditableFields(checkpoint.file_path); setupShowcaseScroll(); setupTabSwitching(); setupTagTooltip(); setupModelNameEditing(checkpoint.file_path); setupBaseModelEditing(checkpoint.file_path); setupFileNameEditing(checkpoint.file_path); // If we have a model ID but no description, fetch it if (checkpoint.civitai?.modelId && !checkpoint.modelDescription) { loadModelDescription(checkpoint.civitai.modelId, checkpoint.file_path); } // Load example images asynchronously loadExampleImages(checkpoint.civitai?.images, checkpoint.sha256); } /** * Load example images asynchronously * @param {Array} images - Array of image objects * @param {string} modelHash - Model hash for fetching local files */ async function loadExampleImages(images, modelHash) { try { const showcaseTab = document.getElementById('showcase-tab'); if (!showcaseTab) return; // First fetch local example files let localFiles = []; if (modelHash) { try { localFiles = await getExampleImageFiles(modelHash); } catch (error) { console.error("Failed to get example files:", error); } } // Then render with both remote images and local files showcaseTab.innerHTML = renderShowcaseContent(images, localFiles); // Re-initialize the showcase event listeners const carousel = showcaseTab.querySelector('.carousel'); if (carousel) { // Only initialize if we actually have examples and they're expanded if (!carousel.classList.contains('collapsed')) { initLazyLoading(carousel); initNsfwBlurHandlers(carousel); initMetadataPanelHandlers(carousel); } } } catch (error) { console.error('Error loading example images:', error); const showcaseTab = document.getElementById('showcase-tab'); if (showcaseTab) { showcaseTab.innerHTML = ` `; } } } /** * Set up editable fields in the checkpoint modal * @param {string} filePath - The full file path of the model. */ function setupEditableFields(filePath) { const editableFields = document.querySelectorAll('.editable-field [contenteditable]'); editableFields.forEach(field => { field.addEventListener('focus', function() { if (this.textContent === 'Add your notes here...') { this.textContent = ''; } }); field.addEventListener('blur', function() { if (this.textContent.trim() === '') { if (this.classList.contains('notes-content')) { this.textContent = 'Add your notes here...'; } } }); }); // Add keydown event listeners for notes const notesContent = document.querySelector('.notes-content'); if (notesContent) { notesContent.addEventListener('keydown', async function(e) { if (e.key === 'Enter') { if (e.shiftKey) { // Allow shift+enter for new line return; } e.preventDefault(); await saveNotes(filePath); } }); } } /** * Save checkpoint notes * @param {string} filePath - Path to the checkpoint file */ async function saveNotes(filePath) { const content = document.querySelector('.notes-content').textContent; try { await saveModelMetadata(filePath, { notes: content }); // Update the corresponding checkpoint card's dataset updateCheckpointCard(filePath, { notes: content }); showToast('Notes saved successfully', 'success'); } catch (error) { showToast('Failed to save notes', 'error'); } } // Export the checkpoint modal API const checkpointModal = { show: showCheckpointModal, toggleShowcase, scrollToTop }; export { checkpointModal }; // Define global functions for use in HTML window.toggleShowcase = function(element) { toggleShowcase(element); }; window.scrollToTopCheckpoint = function(button) { scrollToTop(button); }; window.saveCheckpointNotes = function(filePath) { saveNotes(filePath); };