/** * UI Manager for the arXivCSRAG application * Handles UI elements, modals, and rendering */ class UIManager { constructor() { // Cache DOM elements this.apiKeysModal = document.getElementById('api-keys-modal'); this.paperInfoModal = document.getElementById('paper-info-modal'); this.citationsModal = document.getElementById('citations-modal'); this.loadingOverlay = document.getElementById('loading-overlay'); this.loadingMessage = document.getElementById('loading-message'); this.resultsContainer = document.getElementById('results-container'); this.chatMessages = document.getElementById('chat-messages'); this.subjectTagsSelect = document.getElementById('subject-tags-select'); // Initialize UI this.initEventListeners(); this.loadSubjectTags(); } /** * Initialize event listeners for UI elements */ initEventListeners() { // Modal close buttons document.querySelectorAll('.close-modal, .close-btn').forEach(button => { button.addEventListener('click', () => { this.hideModal(this.apiKeysModal); this.hideModal(this.paperInfoModal); this.hideModal(this.citationsModal); }); }); // Configure API Keys button document.getElementById('configure-api-keys-btn').addEventListener('click', () => { this.showModal(this.apiKeysModal); }); // Window click to close modals window.addEventListener('click', (event) => { if (event.target === this.apiKeysModal) { this.hideModal(this.apiKeysModal); } else if (event.target === this.paperInfoModal) { this.hideModal(this.paperInfoModal); } else if (event.target === this.citationsModal) { this.hideModal(this.citationsModal); } }); } /** * Load subject tags from JSON file */ async loadSubjectTags() { try { const response = await fetch('/data/arxiv_cs_subjects.json'); const subjects = await response.json(); // Clear existing options this.subjectTagsSelect.innerHTML = ''; // Add options to select subjects.forEach(subject => { const option = document.createElement('option'); option.value = subject.tag; option.textContent = `${subject.tag}: ${subject.name}`; this.subjectTagsSelect.appendChild(option); }); } catch (error) { console.error('Error loading subject tags:', error); } } /** * Show a modal * @param {HTMLElement} modal - The modal to show */ showModal(modal) { modal.style.display = 'block'; } /** * Hide a modal * @param {HTMLElement} modal - The modal to hide */ hideModal(modal) { modal.style.display = 'none'; } /** * Show the loading overlay * @param {string} message - The loading message to display */ showLoading(message = 'Processing...') { this.loadingMessage.textContent = message; this.loadingOverlay.style.display = 'flex'; } /** * Hide the loading overlay */ hideLoading() { this.loadingOverlay.style.display = 'none'; } /** * Render search results * @param {Array} papers - The papers to render */ renderSearchResults(papers) { if (!papers || papers.length === 0) { this.resultsContainer.innerHTML = '

No papers found matching your criteria.

'; return; } this.resultsContainer.innerHTML = ''; papers.forEach(paper => { const paperElement = document.createElement('div'); paperElement.className = 'paper-item'; paperElement.dataset.paperId = paper.arxiv_id; const categories = paper.categories.map(cat => `${cat}` ).join(''); paperElement.innerHTML = `

${paper.title}

${paper.published}

${paper.authors.join(', ')}

${categories}
`; paperElement.addEventListener('click', () => { this.showPaperInfo(paper); }); this.resultsContainer.appendChild(paperElement); }); } /** * Show paper information in the modal * @param {Object} paper - The paper data */ showPaperInfo(paper) { // Set modal content document.getElementById('paper-title').textContent = paper.title; document.getElementById('paper-authors').textContent = paper.authors.join(', '); document.getElementById('paper-published').textContent = paper.published; document.getElementById('paper-categories').textContent = paper.categories.join(', '); document.getElementById('paper-id').textContent = paper.arxiv_id; document.getElementById('paper-abstract').textContent = paper.abstract; // Set data attributes for buttons const openArxivBtn = document.getElementById('open-arxiv-btn'); const viewPdfBtn = document.getElementById('view-pdf-btn'); const downloadPdfBtn = document.getElementById('download-pdf-btn'); const chatWithPaperBtn = document.getElementById('chat-with-paper-btn'); openArxivBtn.dataset.url = paper.entry_id; viewPdfBtn.dataset.url = paper.pdf_url; downloadPdfBtn.dataset.paperId = paper.arxiv_id; chatWithPaperBtn.dataset.paperId = paper.arxiv_id; // Show the modal this.showModal(this.paperInfoModal); } /** * Add a message to the chat interface * @param {string} message - The message text * @param {boolean} isUser - Whether the message is from the user * @param {Object} citations - Citations from the AI response */ addChatMessage(message, isUser, citations = null) { const messageElement = document.createElement('div'); messageElement.className = `message ${isUser ? 'user-message' : 'bot-message'}`; // If it's a bot message, parse markdown (simple version) if (!isUser) { // Simple markdown parsing for links, bold, italic, code const formattedMessage = message .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/`(.*?)`/g, '$1') .replace(/\n/g, '
'); messageElement.innerHTML = formattedMessage; // Add citations if available if (citations && (citations.texts.length > 0 || citations.images.length > 0 || citations.tables.length > 0)) { const citationsElement = document.createElement('div'); citationsElement.className = 'citations'; // Add sources section with View All button const sourcesHeader = document.createElement('div'); sourcesHeader.className = 'citations-header'; sourcesHeader.innerHTML = ` `; citationsElement.appendChild(sourcesHeader); messageElement.appendChild(citationsElement); // Add view all functionality const viewAllBtn = sourcesHeader.querySelector('.view-all-citations-btn'); viewAllBtn.addEventListener('click', () => { this.showCitationsModal('Response to: ' + message, citations); }); } } else { messageElement.textContent = message; } this.chatMessages.appendChild(messageElement); this.chatMessages.scrollTop = this.chatMessages.scrollHeight; } /** * Clear the chat messages */ clearChat() { this.chatMessages.innerHTML = ''; this.addSystemMessage('Chat reset. You can now ask questions about the paper.'); } /** * Add a system message to the chat * @param {string} message - The system message */ addSystemMessage(message) { const systemMessage = document.createElement('div'); systemMessage.className = 'system-message'; systemMessage.innerHTML = `

${message}

`; this.chatMessages.appendChild(systemMessage); } /** * Enable chat functionality */ enableChat() { document.getElementById('chat-input').disabled = false; document.getElementById('send-message-btn').disabled = false; document.getElementById('reset-chat-btn').disabled = false; } /** * Disable chat functionality */ disableChat() { document.getElementById('chat-input').disabled = true; document.getElementById('send-message-btn').disabled = true; document.getElementById('reset-chat-btn').disabled = true; } /** * Show citations in a modal * @param {string} query - The query that generated these citations * @param {Object} citations - The citations object with texts, tables, and images */ showCitationsModal(query, citations) { // Cache DOM elements const citationsModal = document.getElementById('citations-modal'); const citationQuery = document.getElementById('citation-query'); const citationsContainer = document.getElementById('citations-container'); // Set the query citationQuery.textContent = `"${query}"`; // Clear previous citations citationsContainer.innerHTML = ''; // Add text citations if (citations.texts.length > 0) { const textSection = document.createElement('div'); textSection.className = 'citation-section'; textSection.innerHTML = '

Text Excerpts

'; const textList = document.createElement('div'); textList.className = 'citation-list'; citations.texts.forEach((text, index) => { const textItem = document.createElement('div'); textItem.className = 'text-citation'; textItem.innerHTML = `
${index + 1}
${text}
`; textList.appendChild(textItem); }); textSection.appendChild(textList); citationsContainer.appendChild(textSection); } // Add table citations if (citations.tables.length > 0) { const tableSection = document.createElement('div'); tableSection.className = 'citation-section'; tableSection.innerHTML = '

Tables

'; const tableList = document.createElement('div'); tableList.className = 'citation-list'; citations.tables.forEach((tableHtml, index) => { const tableItem = document.createElement('div'); tableItem.className = 'table-citation'; // Wrap the table in a container for better responsiveness tableItem.innerHTML = `
${index + 1}
${tableHtml}
`; tableList.appendChild(tableItem); }); tableSection.appendChild(tableList); citationsContainer.appendChild(tableSection); } // Add image citations if (citations.images.length > 0) { const imageSection = document.createElement('div'); imageSection.className = 'citation-section'; imageSection.innerHTML = '

Images

'; const imageList = document.createElement('div'); imageList.className = 'citation-list'; citations.images.forEach((imageBase64, index) => { const imageItem = document.createElement('div'); imageItem.className = 'image-citation'; imageItem.innerHTML = `
${index + 1}
Citation image ${index + 1}
`; imageList.appendChild(imageItem); }); imageSection.appendChild(imageList); citationsContainer.appendChild(imageSection); } // Show the modal this.showModal(citationsModal); } }