// --- START OF FILE modals.js --- // static/js/ui/modals.js import { dom } from './dom.js'; let currentGalleryImages = []; let currentGalleryIndex = 0; // تابع کمکی برای ساخت آیتمهای منو const createMenuItem = (options) => { const { action, format = '', text, icon, isDanger = false, type = 'button' } = options; const element = document.createElement(type); element.className = `menu-item ${isDanger ? 'danger' : ''}`; element.dataset.action = action; if (format) element.dataset.format = format; // اضافه کردن آیکون و متن + لودینگ مخفی element.innerHTML = `${icon}${text}
`; return element; }; // تابع نمایش تصویر در گالری (بدون تغییر) function showImageInGallery(index) { if (index < 0 || index >= currentGalleryImages.length) return; currentGalleryIndex = index; const newImageUrl = currentGalleryImages[index]; dom.galleryMainImage.style.opacity = '0'; setTimeout(() => { dom.galleryMainImage.src = newImageUrl; dom.galleryMainImage.style.opacity = '1'; }, 150); const thumbnails = dom.galleryThumbnails.querySelectorAll('.gallery-thumb'); thumbnails.forEach((thumb, i) => { thumb.classList.toggle('active', i === index); if (i === index) { thumb.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } }); } const handleGalleryKeyDown = (e) => { if (e.key === 'ArrowRight') showImageInGallery((currentGalleryIndex + 1) % currentGalleryImages.length); else if (e.key === 'ArrowLeft') showImageInGallery((currentGalleryIndex - 1 + currentGalleryImages.length) % currentGalleryImages.length); else if (e.key === 'Escape') closeImageGallery(); }; export function openImageGallery(imageUrlsString, startIndex) { try { currentGalleryImages = JSON.parse(imageUrlsString); } catch (e) { console.error("Failed to parse image URLs for gallery:", e); return; } if (!currentGalleryImages || currentGalleryImages.length === 0) return; dom.galleryThumbnails.innerHTML = ''; currentGalleryImages.forEach((url, index) => { const thumb = document.createElement('img'); thumb.src = url; thumb.className = 'gallery-thumb'; thumb.onclick = () => showImageInGallery(index); dom.galleryThumbnails.appendChild(thumb); }); showImageInGallery(startIndex); dom.imageGalleryModal.classList.remove('hidden'); requestAnimationFrame(() => { dom.imageGalleryModal.classList.add('visible'); }); dom.galleryCloseBtn.onclick = closeImageGallery; dom.imageGalleryModal.onclick = (e) => { if (e.target === dom.imageGalleryModal) closeImageGallery(); }; dom.galleryNextBtn.onclick = () => showImageInGallery((currentGalleryIndex + 1) % currentGalleryImages.length); dom.galleryPrevBtn.onclick = () => showImageInGallery((currentGalleryIndex - 1 + currentGalleryImages.length) % currentGalleryImages.length); window.addEventListener('keydown', handleGalleryKeyDown); } function closeImageGallery() { dom.imageGalleryModal.classList.remove('visible'); setTimeout(() => { dom.imageGalleryModal.classList.add('hidden'); dom.galleryMainImage.src = ''; }, 300); window.removeEventListener('keydown', handleGalleryKeyDown); } export function toggleSidebar(show) { if (show) { dom.sidebarOverlay.classList.remove('hidden'); requestAnimationFrame(() => { dom.sidebarOverlay.style.opacity = '1'; dom.historySidebar.style.transform = 'translateX(0)'; }); } else { dom.sidebarOverlay.style.opacity = '0'; dom.historySidebar.style.transform = 'translateX(100%)'; setTimeout(() => dom.sidebarOverlay.classList.add('hidden'), 300); } } export function toggleEditModal(show) { if (show) { dom.editModal.classList.remove('hidden'); requestAnimationFrame(() => { dom.editModalOverlay.style.opacity = '1'; dom.editModalContent.style.opacity = '1'; dom.editModalContent.style.transform = 'scale(1)'; dom.editInput.focus(); }); } else { dom.editModalOverlay.style.opacity = '0'; dom.editModalContent.style.opacity = '0'; dom.editModalContent.style.transform = 'scale(0.95)'; setTimeout(() => dom.editModal.classList.add('hidden'), 300); } } export function toggleHtmlPreviewModal(show, htmlContent = '') { if (show) { dom.htmlPreviewIframe.srcdoc = htmlContent; dom.htmlPreviewModal.classList.remove('hidden'); requestAnimationFrame(() => { dom.htmlPreviewOverlay.style.opacity = '1'; dom.htmlPreviewContent.style.opacity = '1'; dom.htmlPreviewContent.style.transform = 'scale(1)'; }); } else { dom.htmlPreviewOverlay.style.opacity = '0'; dom.htmlPreviewContent.style.opacity = '0'; dom.htmlPreviewContent.style.transform = 'scale(0.95)'; setTimeout(() => { dom.htmlPreviewModal.classList.add('hidden'); dom.htmlPreviewIframe.srcdoc = ''; }, 300); } } // *** تابع اصلی که منوی سه نقطه تاریخچه را میسازد (به روز شده) *** export function showHistoryMenu(event, sessionId) { event.stopPropagation(); const menu = dom.historyItemMenu; // تعریف دقیق آیتمها به ترتیب خواسته شده menu.innerHTML = ` ${createMenuItem({ action: 'rename', text: 'تغییر نام گفتگو', icon: `` }).outerHTML} ${createMenuItem({ action: 'convert-chat', format: 'pdf', text: 'تبدیل به PDF', icon: `` }).outerHTML} ${createMenuItem({ action: 'convert-chat', format: 'docx', text: 'تبدیل به Word', icon: `` }).outerHTML} ${createMenuItem({ action: 'convert-chat', format: 'txt', text: 'تبدیل به Text', icon: `` }).outerHTML} ${createMenuItem({ action: 'convert-chat', format: 'html', text: 'تبدیل به HTML', icon: `` }).outerHTML} ${createMenuItem({ action: 'delete', text: 'حذف گفتگو', icon: ``, isDanger: true }).outerHTML} `; menu.dataset.sessionId = sessionId; // تنظیم موقعیت منو const buttonRect = event.currentTarget.getBoundingClientRect(); const menuHeight = 350; // ارتفاع تخمینی منو با گزینههای بیشتر const margin = 8; let top = buttonRect.bottom + margin; let right = window.innerWidth - buttonRect.right; // اگر پایین صفحه جا نیست، منو را به سمت بالا باز کن if (top + menuHeight > window.innerHeight) { top = buttonRect.top - menuHeight - margin; } // جلوگیری از بیرون زدن از بالای صفحه if (top < 0) top = 10; menu.style.top = `${top}px`; menu.style.right = `${right}px`; menu.style.left = 'auto'; menu.style.transformOrigin = (top > buttonRect.top) ? 'top right' : 'bottom right'; menu.classList.add('visible'); const closeMenu = () => { menu.classList.remove('visible'); window.removeEventListener('click', closeMenu); }; window.addEventListener('click', closeMenu, { once: true }); } export function showMessageMenu(event, messageIndex, activeChat, escapeHTML) { event.stopPropagation(); const menu = dom.messageItemMenu; const menuContent = dom.messageItemMenuContent; const message = activeChat.messages[messageIndex]; if (!message) return; const textPart = message.parts.find(p => p.text); const textContent = textPart ? textPart.text : '[محتوای غیر متنی]'; // برای منوی پیام تکی هم همان گزینهها را با استفاده از تابع کمکی میسازیم let menuItemsHtml = ''; if (message.role === 'assistant' && textPart) { menuItemsHtml += createMenuItem({ action: 'convert-message', format: 'pdf', text: 'تبدیل به PDF', icon: `` }).outerHTML; menuItemsHtml += createMenuItem({ action: 'convert-message', format: 'docx', text: 'تبدیل به Word', icon: `` }).outerHTML; menuItemsHtml += createMenuItem({ action: 'convert-message', format: 'txt', text: 'تبدیل به Text', icon: `` }).outerHTML; menuItemsHtml += createMenuItem({ action: 'convert-message', format: 'html', text: 'تبدیل به HTML', icon: `` }).outerHTML; menuItemsHtml += ''; } menuItemsHtml += createMenuItem({ action: 'delete-message', text: 'حذف پیام', icon: ``, isDanger: true }).outerHTML; const escapedContent = escapeHTML(textContent); menuContent.innerHTML = ` ${menuItemsHtml} `; menu.dataset.messageIndex = messageIndex; menu.classList.remove('hidden'); requestAnimationFrame(() => { menu.classList.add('visible'); }); } export function showConfirmModal(message, onConfirm) { dom.confirmModalMessage.textContent = message; dom.confirmModal.classList.remove('hidden'); requestAnimationFrame(() => { dom.confirmModalOverlay.style.opacity = '1'; dom.confirmModalContent.style.opacity = '1'; dom.confirmModalContent.style.transform = 'scale(1)'; }); const hide = () => { dom.confirmModalOverlay.style.opacity = '0'; dom.confirmModalContent.style.opacity = '0'; dom.confirmModalContent.style.transform = 'scale(0.95)'; setTimeout(() => dom.confirmModal.classList.add('hidden'), 300); }; dom.confirmModalConfirmBtn.onclick = () => { onConfirm(); hide(); }; dom.confirmModalCancelBtn.onclick = hide; dom.confirmModalOverlay.onclick = hide; } export function showRenameModal(currentTitle, onConfirm) { dom.renameInput.value = currentTitle; dom.renameModal.classList.remove('hidden'); requestAnimationFrame(() => { dom.renameModalOverlay.style.opacity = '1'; dom.renameModalContent.style.opacity = '1'; dom.renameModalContent.style.transform = 'scale(1)'; dom.renameInput.focus(); dom.renameInput.select(); }); const hide = () => { dom.renameModalOverlay.style.opacity = '0'; dom.renameModalContent.style.opacity = '0'; dom.renameModalContent.style.transform = 'scale(0.95)'; setTimeout(() => dom.renameModal.classList.add('hidden'), 300); }; dom.renameModalContent.onsubmit = (e) => { e.preventDefault(); const newTitle = dom.renameInput.value.trim(); if (newTitle) { onConfirm(newTitle); } hide(); }; dom.renameModalCancelBtn.onclick = hide; dom.renameModalOverlay.onclick = hide; } export function showEditModal(currentText, onConfirm) { dom.editInput.value = currentText; toggleEditModal(true); dom.editModalContent.onsubmit = (e) => { e.preventDefault(); const newText = dom.editInput.value.trim(); if (newText === '') { showConfirmModal('متن پیام شما خالی است. آیا مایل به حذف پیام هستید؟', () => { onConfirm(''); }); } else if (newText !== currentText) { onConfirm(newText); } toggleEditModal(false); }; dom.editModalCancelBtn.onclick = () => toggleEditModal(false); dom.editModalOverlay.onclick = () => toggleEditModal(false); } export function toggleSettingsModal(show) { const modal = dom.settingsModal; const content = dom.settingsModalContent; if (show) { modal.classList.remove('hidden'); requestAnimationFrame(() => { modal.style.opacity = '1'; content.style.opacity = '1'; content.style.transform = 'scale(1)'; }); } else { modal.style.opacity = '0'; content.style.opacity = '0'; content.style.transform = 'scale(0.95)'; setTimeout(() => modal.classList.add('hidden'), 200); } } export function updateSettingsUI(isPremium) { if (!dom.settingsUserTier) return; let content = ''; if (isPremium) { const premiumIcon = ``; content = `