import { showToast } from '../utils/uiHelpers.js'; import { modalManager } from '../managers/ModalManager.js'; import { state } from '../state/index.js'; export function createLoraCard(lora) { const card = document.createElement('div'); card.className = 'lora-card'; card.dataset.sha256 = lora.sha256; card.dataset.filepath = lora.file_path; card.dataset.name = lora.model_name; card.dataset.file_name = lora.file_name; card.dataset.folder = lora.folder; card.dataset.modified = lora.modified; card.dataset.from_civitai = lora.from_civitai; card.dataset.base_model = lora.base_model; card.dataset.meta = JSON.stringify(lora.civitai || {}); const version = state.previewVersions.get(lora.file_path); const previewUrl = lora.preview_url || '/loras_static/images/no-preview.png'; const versionedPreviewUrl = version ? `${previewUrl}?t=${version}` : previewUrl; card.innerHTML = `
${previewUrl.endsWith('.mp4') ? `` : `${lora.model_name}` }
${lora.base_model}
`; // Main card click event card.addEventListener('click', () => { const loraMeta = { sha256: card.dataset.sha256, file_path: card.dataset.filepath.replace(/[^/]+$/, ''), // Extract directory path model_name: card.dataset.name, file_name: card.dataset.file_name, folder: card.dataset.folder, modified: card.dataset.modified, from_civitai: card.dataset.from_civitai === 'true', base_model: card.dataset.base_model, civitai: JSON.parse(card.dataset.meta || '{}') }; showLoraModal(loraMeta); }); // Copy button click event card.querySelector('.fa-copy')?.addEventListener('click', e => { e.stopPropagation(); navigator.clipboard.writeText(card.dataset.file_name) .then(() => showToast('Model name copied', 'success')) .catch(() => showToast('Copy failed', 'error')); }); // Civitai button click event if (lora.from_civitai) { card.querySelector('.fa-globe')?.addEventListener('click', e => { e.stopPropagation(); openCivitai(lora.model_name); }); } // Delete button click event card.querySelector('.fa-trash')?.addEventListener('click', e => { e.stopPropagation(); deleteModel(lora.file_path); }); // Replace preview button click event card.querySelector('.fa-image')?.addEventListener('click', e => { e.stopPropagation(); replacePreview(lora.file_path); }); return card; } export function showLoraModal(lora) { const escapedWords = lora.trainedWords?.length ? lora.trainedWords.map(word => word.replace(/'/g, '\\\'')) : []; const content = ` `; modalManager.showModal('loraModal', content); setupEditableFields(); } function setupEditableFields() { const editableFields = document.querySelectorAll('.editable-field [contenteditable]'); editableFields.forEach(field => { field.addEventListener('focus', function() { if (this.textContent === 'Add your notes here...' || this.textContent === 'Save usage tips here..') { this.textContent = ''; } }); field.addEventListener('blur', function() { if (this.textContent.trim() === '') { this.textContent = this.classList.contains('usage-tips-content') ? 'Save usage tips here..' : 'Add your notes here...'; } }); }); } // Add these functions to handle saving the editable fields window.saveUsageTips = async function(filePath) { const content = document.querySelector('.usage-tips-content').textContent; try { await saveModelMetadata(filePath, { usage_tips: content }); showToast('Usage tips saved successfully', 'success'); } catch (error) { showToast('Failed to save usage tips', 'error'); } }; window.saveNotes = async function(filePath) { const content = document.querySelector('.notes-content').textContent; try { await saveModelMetadata(filePath, { notes: content }); showToast('Notes saved successfully', 'success'); } catch (error) { showToast('Failed to save notes', 'error'); } }; async function saveModelMetadata(filePath, data) { const response = await fetch('/loras/api/save-metadata', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ file_path: filePath, ...data }) }); if (!response.ok) { throw new Error('Failed to save metadata'); } } function renderTriggerWords(words) { if (!words.length) return `
No trigger word needed
`; return `
${words.map(word => `
${word}
`).join('')}
`; } function renderShowcaseImages(images) { if (!images?.length) return ''; return `
Show ${images.length} examples
`; } // Add this to the window object for global access export function toggleShowcase(element) { const carousel = element.nextElementSibling; const isCollapsed = carousel.classList.contains('collapsed'); const indicator = element.querySelector('span'); const icon = element.querySelector('i'); carousel.classList.toggle('collapsed'); if (isCollapsed) { indicator.textContent = 'Hide examples'; icon.classList.replace('fa-chevron-down', 'fa-chevron-up'); initLazyLoading(carousel); } else { const count = carousel.querySelectorAll('.media-wrapper').length; indicator.textContent = `Show ${count} examples`; icon.classList.replace('fa-chevron-up', 'fa-chevron-down'); } }; // Add lazy loading initialization export function initLazyLoading(container) { const lazyElements = container.querySelectorAll('.lazy'); const lazyLoad = (element) => { if (element.tagName.toLowerCase() === 'video') { element.src = element.dataset.src; element.querySelector('source').src = element.dataset.src; element.load(); } else { element.src = element.dataset.src; } element.classList.remove('lazy'); }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { lazyLoad(entry.target); observer.unobserve(entry.target); } }); }); lazyElements.forEach(element => observer.observe(element)); }