diff --git "a/static/js/script.js" "b/static/js/script.js"
new file mode 100644--- /dev/null
+++ "b/static/js/script.js"
@@ -0,0 +1,2867 @@
+// Research Radar - Enhanced Interactive JavaScript
+class ResearchRadar {
+ constructor() {
+ this.currentSection = 'search';
+ this.currentPage = 'landing';
+ this.isLoading = false;
+ this.chatHistory = [];
+ this.currentDocumentId = null;
+ this.uploadProgress = 0;
+ this.searchCache = new Map();
+
+ this.init();
+ }
+
+ init() {
+ this.setupEventListeners();
+ this.setupDragAndDrop();
+ this.initializeChat();
+ this.setupChatInput();
+ this.setupSearchSuggestions();
+ this.updateStatusIndicator();
+ this.setupPageNavigation();
+ this.setupEnhancedChat();
+ this.setupMobileNav();
+ this.setupVhUnit();
+ }
+
+ setupEventListeners() {
+ console.log('Setting up event listeners...');
+
+ // Landing page CTA buttons - Critical buttons that need to work
+ this.setupLandingPageButtons();
+
+ // Navigation for app page
+ const navLinks = document.querySelectorAll('.app-nav .nav-link');
+ console.log(`Found ${navLinks.length} navigation links`);
+ navLinks.forEach(link => {
+ link.addEventListener('click', (e) => {
+ e.preventDefault();
+ const section = link.dataset.section;
+ console.log(`Navigation clicked: ${section}`);
+ if (section) {
+ this.switchSection(section);
+ }
+ });
+ });
+
+ // Quick search cards
+ const quickSearchCards = document.querySelectorAll('.quick-search-card');
+ console.log(`Found ${quickSearchCards.length} quick search cards`);
+ quickSearchCards.forEach(card => {
+ card.addEventListener('click', (e) => {
+ const query = card.dataset.query;
+ console.log(`Quick search card clicked: ${query}`);
+ if (query) {
+ const searchInput = document.getElementById('searchInput');
+ if (searchInput) {
+ searchInput.value = query;
+ this.searchPapers();
+ }
+ }
+ });
+ });
+
+ // Chat suggestions
+ document.querySelectorAll('.suggestion-chip').forEach(chip => {
+ chip.addEventListener('click', (e) => {
+ const question = chip.dataset.question;
+ if (question) {
+ document.getElementById('chatInput').value = question;
+ this.sendChatMessage();
+ }
+ });
+ });
+
+ // Example URLs
+ document.querySelectorAll('.example-url').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ const url = btn.dataset.url;
+ if (url) {
+ document.getElementById('paperUrl').value = url;
+ }
+ });
+ });
+
+ // Clear chat button
+ const clearBtn = document.getElementById('chatClearBtn');
+ if (clearBtn) {
+ clearBtn.addEventListener('click', () => this.clearChat());
+ }
+
+ // Search functionality
+ const searchBtn = document.getElementById('searchBtn');
+ const searchInput = document.getElementById('searchInput');
+
+ console.log('Search button found:', !!searchBtn);
+ console.log('Search input found:', !!searchInput);
+
+ if (searchBtn) {
+ searchBtn.addEventListener('click', () => {
+ console.log('Search button clicked!');
+ this.searchPapers();
+ });
+ }
+
+ if (searchInput) {
+ searchInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ console.log('Search input Enter pressed!');
+ this.searchPapers();
+ }
+ });
+ }
+
+ // File upload
+ const fileInput = document.getElementById('fileInput');
+ console.log('File input found:', !!fileInput);
+ if (fileInput) {
+ fileInput.addEventListener('change', (e) => {
+ console.log('File input changed!');
+ this.handleFileUpload(e);
+ });
+ }
+
+ // URL analysis
+ const analyzeUrlBtn = document.getElementById('analyzeUrlBtn');
+ const paperUrlInput = document.getElementById('paperUrl');
+
+ console.log('Analyze URL button found:', !!analyzeUrlBtn);
+ console.log('Paper URL input found:', !!paperUrlInput);
+
+ if (analyzeUrlBtn) {
+ analyzeUrlBtn.addEventListener('click', () => {
+ console.log('Analyze URL button clicked!');
+ this.analyzePaperUrl();
+ });
+ }
+
+ if (paperUrlInput) {
+ paperUrlInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ console.log('Paper URL input Enter pressed!');
+ this.analyzePaperUrl();
+ }
+ });
+ }
+
+ // Chat functionality
+ const chatSendBtn = document.getElementById('chatSendBtn');
+ const chatInput = document.getElementById('chatInput');
+ const chatSendBtnPanel = document.getElementById('chatSendBtnPanel');
+ const chatInputPanel = document.getElementById('chatInputPanel');
+
+ if (chatSendBtn) {
+ chatSendBtn.addEventListener('click', () => this.sendChatMessage());
+ }
+
+ if (chatInput) {
+ chatInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ this.sendChatMessage();
+ }
+ });
+ }
+
+ // Panel chat functionality
+ if (chatSendBtnPanel) {
+ chatSendBtnPanel.addEventListener('click', () => this.sendChatMessagePanel());
+ }
+
+ if (chatInputPanel) {
+ chatInputPanel.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ this.sendChatMessagePanel();
+ }
+ });
+ }
+ }
+
+ setupLandingPageButtons() {
+ console.log('Setting up landing page buttons...');
+
+ // Get Started button (nav)
+ const getStartedBtn = document.querySelector('.nav-cta-btn');
+ console.log('Get Started button found:', !!getStartedBtn);
+ if (getStartedBtn) {
+ // Remove existing onclick to prevent conflicts
+ getStartedBtn.removeAttribute('onclick');
+ getStartedBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log('Get Started button clicked!');
+ this.navigateToApp('search');
+ });
+ }
+
+ // Start Exploring button
+ const startExploringBtn = document.querySelector('.cta-button.primary');
+ console.log('Start Exploring button found:', !!startExploringBtn);
+ if (startExploringBtn) {
+ // Remove existing onclick to prevent conflicts
+ startExploringBtn.removeAttribute('onclick');
+ startExploringBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log('Start Exploring button clicked!');
+ this.navigateToApp('search');
+ });
+ }
+
+ // Upload Paper button
+ const uploadPaperBtn = document.querySelector('.cta-button.secondary');
+ console.log('Upload Paper button found:', !!uploadPaperBtn);
+ if (uploadPaperBtn) {
+ // Remove existing onclick to prevent conflicts
+ uploadPaperBtn.removeAttribute('onclick');
+ uploadPaperBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log('Upload Paper button clicked!');
+ this.navigateToApp('upload');
+ });
+ }
+
+ // Back to Landing button
+ const backToLandingBtn = document.querySelector('.back-to-landing');
+ console.log('Back to Landing button found:', !!backToLandingBtn);
+ if (backToLandingBtn) {
+ // Remove existing onclick to prevent conflicts
+ backToLandingBtn.removeAttribute('onclick');
+ backToLandingBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log('Back to Landing button clicked!');
+ this.navigateToLanding();
+ });
+ }
+
+ // Brand logo that navigates to landing
+ const brandLogo = document.querySelector('.app-nav .nav-brand');
+ console.log('Brand logo found:', !!brandLogo);
+ if (brandLogo) {
+ // Remove existing onclick to prevent conflicts
+ brandLogo.removeAttribute('onclick');
+ brandLogo.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log('Brand logo clicked!');
+ this.navigateToLanding();
+ });
+ }
+ }
+
+ setupDragAndDrop() {
+ const uploadZone = document.getElementById('uploadZone');
+ if (!uploadZone) return;
+
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
+ uploadZone.addEventListener(eventName, this.preventDefaults, false);
+ });
+
+ ['dragenter', 'dragover'].forEach(eventName => {
+ uploadZone.addEventListener(eventName, () => {
+ uploadZone.classList.add('drag-over');
+ }, false);
+ });
+
+ ['dragleave', 'drop'].forEach(eventName => {
+ uploadZone.addEventListener(eventName, () => {
+ uploadZone.classList.remove('drag-over');
+ }, false);
+ });
+
+ uploadZone.addEventListener('drop', (e) => {
+ const files = e.dataTransfer.files;
+ if (files.length > 0) {
+ this.processFile(files[0]);
+ }
+ }, false);
+ }
+
+ preventDefaults(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ switchSection(sectionName) {
+ // Update navigation
+ document.querySelectorAll('.nav-link').forEach(link => {
+ link.classList.remove('active');
+ });
+
+ document.querySelector(`[data-section="${sectionName}"]`).classList.add('active');
+
+ // Update sections
+ document.querySelectorAll('.section').forEach(section => {
+ section.classList.remove('active');
+ });
+
+ document.getElementById(sectionName).classList.add('active');
+
+ this.currentSection = sectionName;
+ }
+
+ showLoading(show = true) {
+ const overlay = document.getElementById('loadingOverlay');
+ if (overlay) {
+ if (show) {
+ overlay.classList.add('active');
+ this.isLoading = true;
+ } else {
+ overlay.classList.remove('active');
+ this.isLoading = false;
+ }
+ }
+ }
+
+ showToast(message, type = 'info') {
+ const container = document.getElementById('toastContainer');
+ if (!container) return;
+
+ const toast = document.createElement('div');
+ toast.className = `toast ${type}`;
+ toast.innerHTML = `
+
+
+ ${message}
+
+ `;
+
+ container.appendChild(toast);
+
+ // Auto remove after 5 seconds
+ setTimeout(() => {
+ if (toast.parentNode) {
+ toast.style.animation = 'toastSlideOut 0.3s ease-out forwards';
+ setTimeout(() => {
+ container.removeChild(toast);
+ }, 300);
+ }
+ }, 5000);
+ }
+
+ getToastIcon(type) {
+ const icons = {
+ 'success': 'check-circle',
+ 'error': 'exclamation-circle',
+ 'warning': 'exclamation-triangle',
+ 'info': 'info-circle'
+ };
+ return icons[type] || 'info-circle';
+ }
+
+ async searchPapers() {
+ const searchInput = document.getElementById('searchInput');
+ const query = searchInput.value.trim();
+
+ if (!query) {
+ this.showToast('Please enter a search query', 'warning');
+ return;
+ }
+
+ this.showLoading(true);
+
+ try {
+ const response = await fetch('/search', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ query })
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ this.displaySearchResults(data.papers);
+ this.showToast(`Found ${data.papers.length} papers`, 'success');
+ } else {
+ throw new Error(data.error || 'Search failed');
+ }
+ } catch (error) {
+ console.error('Search error:', error);
+ this.showToast(error.message, 'error');
+ } finally {
+ this.showLoading(false);
+ }
+ }
+
+ displaySearchResults(papers) {
+ const container = document.getElementById('searchResults');
+ if (!container) return;
+
+ // Keep papers for later access (e.g., openSummaryChat after ingest/summarize)
+ this.lastSearchResults = papers;
+
+ if (papers.length === 0) {
+ container.innerHTML = `
+
+
+
+ No papers found. Try different keywords.
+
+
+ `;
+ return;
+ }
+
+ container.innerHTML = papers.map((paper, index) => `
+
+
${this.escapeHtml(paper.title)}
+
+
+ ${paper.authors.slice(0, 3).map(author => this.escapeHtml(author)).join(', ')}
+ ${paper.authors.length > 3 ? ` and ${paper.authors.length - 3} others` : ''}
+
+
+ ${paper.published}
+ ${this.escapeHtml(paper.category)}
+
+
+ ${this.truncateText(this.escapeHtml(paper.summary), 300)}
+
+
+
+ `).join('');
+
+ // Add event listeners to Generate Summary buttons
+ this.setupGenerateSummaryButtons();
+ }
+
+ setupGenerateSummaryButtons() {
+ console.log('Setting up Generate Summary buttons...');
+
+ const generateButtons = document.querySelectorAll('.generate-summary-btn');
+ console.log(`Found ${generateButtons.length} Generate Summary buttons`);
+
+ generateButtons.forEach(button => {
+ button.addEventListener('click', async (e) => {
+ e.preventDefault();
+ const paperUrl = button.dataset.paperUrl;
+ const paperIndex = button.dataset.paperIndex;
+ const pdfUrl = button.dataset.paperPdfUrl;
+ const paper = this.lastSearchResults && typeof Number(paperIndex) === 'number'
+ ? this.lastSearchResults[Number(paperIndex)]
+ : null;
+
+ console.log(`Generate Summary button clicked for paper: ${paperUrl}`);
+
+ if (paperUrl) {
+ // Disable button and show loading state
+ button.disabled = true;
+ button.innerHTML = ' Generating...';
+
+ try {
+ // Ingest the paper PDF first, then summarize from doc_id
+ const ingestRes = await fetch('/ingest-paper', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ pdf_url: pdfUrl, url: paperUrl })
+ });
+ const ingestData = await ingestRes.json();
+ if (!ingestRes.ok || !ingestData.success) {
+ throw new Error(ingestData.error || 'Failed to ingest paper');
+ }
+ const docId = ingestData.doc_id;
+ // Persist active document in UI
+ this.setActiveDocument(docId);
+ // Now summarize using doc_id (backend will fetch all chunks from Qdrant)
+ const sumRes = await fetch('/summarize-paper', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ doc_id: docId })
+ });
+ const sumData = await sumRes.json();
+ if (!sumRes.ok || !sumData.success) {
+ throw new Error(sumData.error || 'Failed to generate summary');
+ }
+ // Open summary + chat view
+ const paperData = paper || { title: '', authors: [], published: '', category: '' };
+ this.openSummaryChat({ title: paperData.title, authors: paperData.authors, published: paperData.published, category: paperData.category }, sumData.summary);
+ } catch (error) {
+ console.error('Error generating summary:', error);
+ this.showToast('Failed to generate summary. Please try again.', 'error');
+ } finally {
+ // Re-enable button
+ button.disabled = false;
+ button.innerHTML = ' Generate Summary';
+ }
+ } else {
+ console.error('No paper URL found for Generate Summary button');
+ this.showToast('Error: Paper URL not found', 'error');
+ }
+ });
+ });
+ }
+
+ async handleFileUpload(event) {
+ const file = event.target.files[0];
+ if (file) {
+ await this.processFile(file);
+ }
+ }
+
+ async processFile(file) {
+ // Validate file
+ const allowedTypes = ['application/pdf', 'text/plain', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
+ const maxSize = 16 * 1024 * 1024; // 16MB
+
+ if (!allowedTypes.includes(file.type)) {
+ this.showToast('Invalid file type. Please upload PDF, TXT, or DOCX files.', 'error');
+ return;
+ }
+
+ if (file.size > maxSize) {
+ this.showToast('File too large. Maximum size is 16MB.', 'error');
+ return;
+ }
+
+ this.showLoading(true);
+
+ try {
+ const formData = new FormData();
+ formData.append('file', file);
+
+ const response = await fetch('/upload', {
+ method: 'POST',
+ body: formData
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ this.displayUploadResult(data);
+ this.showToast('File processed successfully!', 'success');
+ } else {
+ throw new Error(data.error || 'Upload failed');
+ }
+ } catch (error) {
+ console.error('Upload error:', error);
+ this.showToast(error.message, 'error');
+ } finally {
+ this.showLoading(false);
+ // Reset file input
+ document.getElementById('fileInput').value = '';
+ }
+ }
+
+ displayUploadResult(data) {
+ // Create paper data object for the uploaded file
+ const paperData = {
+ title: data.filename || 'Uploaded Document',
+ authors: ['Uploaded by User'],
+ published: new Date().toLocaleDateString(),
+ category: 'Uploaded Document',
+ filename: data.filename,
+ word_count: data.word_count
+ };
+
+ // Store the document ID for chat functionality
+ this.currentDocumentId = data.doc_id;
+
+ // Open the Summary + Chat panel with the uploaded document
+ this.openSummaryChat(paperData, data.summary);
+ }
+
+ async analyzePaperUrl() {
+ const urlInput = document.getElementById('paperUrl');
+ const url = urlInput.value.trim();
+
+ if (!url) {
+ this.showToast('Please enter a paper URL', 'warning');
+ return;
+ }
+
+ // Basic URL validation
+ if (!url.includes('arxiv.org')) {
+ this.showToast('Please enter a valid arXiv URL', 'warning');
+ return;
+ }
+
+ this.showLoading(true);
+
+ try {
+ const response = await fetch('/summarize-paper', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ url })
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ this.displayPaperAnalysis(data);
+ this.showToast('Paper analyzed successfully!', 'success');
+ urlInput.value = ''; // Clear input
+ } else {
+ throw new Error(data.error || 'Analysis failed');
+ }
+ } catch (error) {
+ console.error('Analysis error:', error);
+ this.showToast(error.message, 'error');
+ } finally {
+ this.showLoading(false);
+ }
+ }
+
+ displayPaperAnalysis(data) {
+ // Store the document ID for chat functionality
+ this.currentDocumentId = data.doc_id;
+
+ // Open the Summary + Chat panel with the analyzed paper
+ this.openSummaryChat(data.paper, data.summary);
+ }
+
+ async summarizePaper(paperUrl) {
+ // Deprecated in favor of ingest -> summarize flow
+ return this.showToast('Summarization flow updated. Please use Generate Summary button.', 'info');
+ }
+
+ openSummaryChat(paperData, summaryText) {
+ // Hide current section and show summary-chat
+ document.querySelectorAll('.section').forEach(section => {
+ section.style.display = 'none';
+ });
+
+ const summarySection = document.getElementById('summary-chat');
+ summarySection.style.display = 'block';
+
+ // Update paper information
+ document.getElementById('paperTitle').textContent = paperData.title || 'Research Paper';
+ document.getElementById('paperAuthor').textContent = paperData.authors ? paperData.authors.join(', ') : 'Unknown Author';
+ document.getElementById('paperDate').textContent = paperData.published || new Date().getFullYear();
+ document.getElementById('paperCategory').textContent = paperData.category || 'Research';
+
+ // Show summary
+ this.displaySummaryInPanel(summaryText);
+
+ // Setup chat panel
+ this.setupChatPanel();
+
+ // Store current paper data for chat context
+ this.currentPaper = paperData;
+
+ // Default to Chat tab for immediate Q&A after tabs are in DOM
+ setTimeout(() => {
+ try { switchTab('chat'); } catch (_) {}
+ const chatInput = document.getElementById('chatInputPanel');
+ if (chatInput) chatInput.focus();
+ }, 150);
+ }
+
+ displaySummaryInPanel(summaryText) {
+ const summaryLoading = document.getElementById('summaryLoading');
+ const summaryTextEl = document.getElementById('summaryText');
+
+ // Hide loading and show summary
+ summaryLoading.style.display = 'none';
+ summaryTextEl.style.display = 'block';
+ summaryTextEl.innerHTML = this.formatSummaryText(summaryText);
+
+ // Update stats
+ this.updateSummaryStats(summaryText);
+ }
+
+ formatSummaryText(text) {
+ // Convert plain text to formatted HTML
+ return text
+ .replace(/\n\n/g, '')
+ .replace(/\n/g, '
')
+ .replace(/\*\*(.*?)\*\*/g, '$1')
+ .replace(/\*(.*?)\*/g, '$1')
+ .replace(/^/, '
')
+ .replace(/$/, '
');
+ }
+
+ updateSummaryStats(text) {
+ const wordCount = text.split(/\s+/).length;
+ const readingTime = Math.ceil(wordCount / 200); // Average reading speed
+ const compressionRatio = Math.round((1 - (text.length / (text.length * 3))) * 100); // Estimate
+
+ const wc = document.getElementById('wordCount');
+ const rt = document.getElementById('readingTime');
+ const cr = document.getElementById('compressionRatio');
+ if (wc) wc.textContent = wordCount.toLocaleString();
+ if (rt) rt.textContent = `${readingTime} min`;
+ if (cr) cr.textContent = `${compressionRatio}%`;
+ }
+
+ setupChatPanel() {
+ const chatInput = document.getElementById('chatInputPanel');
+ const sendBtn = document.getElementById('chatSendBtnPanel');
+
+ // Clear any existing event listeners
+ const newChatInput = chatInput.cloneNode(true);
+ chatInput.parentNode.replaceChild(newChatInput, chatInput);
+
+ const newSendBtn = sendBtn.cloneNode(true);
+ sendBtn.parentNode.replaceChild(newSendBtn, sendBtn);
+
+ // Add new event listeners
+ newChatInput.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ this.sendChatMessagePanel();
+ }
+ });
+
+ newChatInput.addEventListener('input', () => {
+ this.autoResizeTextarea(newChatInput);
+ });
+
+ newSendBtn.addEventListener('click', () => {
+ this.sendChatMessagePanel();
+ });
+ }
+
+ sendChatMessagePanel() {
+ const chatInput = document.getElementById('chatInputPanel');
+ const message = chatInput.value.trim();
+
+ if (!message) return;
+
+ // Add user message
+ this.addChatMessagePanel(message, 'user');
+
+ // Clear input
+ chatInput.value = '';
+ this.autoResizeTextarea(chatInput);
+
+ // Show typing indicator and send to backend
+ this.showChatTypingPanel();
+ this.sendChatToBackend(message);
+ }
+
+ addChatMessagePanel(message, sender) {
+ const chatPanel = document.getElementById('chatMessagesPanel');
+
+ // Remove welcome message if it exists
+ const welcome = chatPanel.querySelector('.chat-welcome');
+ if (welcome && sender === 'user') {
+ welcome.remove();
+ }
+
+ const messageDiv = document.createElement('div');
+ messageDiv.className = `chat-message-panel ${sender}`;
+
+ const currentTime = new Date().toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+
+ messageDiv.innerHTML = `
+
+
+
+
+
+ ${sender === 'bot' ? message : this.escapeHtml(message)}
+
+
${currentTime}
+
+ `;
+
+ chatPanel.appendChild(messageDiv);
+ chatPanel.scrollTop = chatPanel.scrollHeight;
+ }
+
+ showChatTypingPanel() {
+ const chatPanel = document.getElementById('chatMessagesPanel');
+
+ const typingDiv = document.createElement('div');
+ typingDiv.className = 'chat-message-panel bot';
+ typingDiv.id = 'typingIndicatorPanel';
+
+ typingDiv.innerHTML = `
+
+
+
+
+ `;
+
+ chatPanel.appendChild(typingDiv);
+ chatPanel.scrollTop = chatPanel.scrollHeight;
+ }
+
+ hideChatTypingPanel() {
+ const typingIndicator = document.getElementById('typingIndicatorPanel');
+ if (typingIndicator) {
+ typingIndicator.remove();
+ }
+ }
+
+ sendChatToBackend(message) {
+ fetch('/chat', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ message: message })
+ })
+ .then(response => response.json())
+ .then(data => {
+ this.hideChatTypingPanel();
+
+ if (data.success) {
+ this.addChatMessagePanel(data.response, 'bot');
+ } else {
+ this.addChatMessagePanel(
+ `I apologize, but I encountered an error: ${data.error || 'Unknown error'}. Please try again.`,
+ 'bot'
+ );
+ }
+ })
+ .catch(error => {
+ console.error('Chat error:', error);
+ this.hideChatTypingPanel();
+ this.addChatMessagePanel(
+ 'I apologize, but I\'m having trouble connecting right now. Please check your connection and try again.',
+ 'bot'
+ );
+ });
+ }
+
+ initializeChat() {
+ this.chatHistory = [];
+ }
+
+ async sendChatMessage() {
+ // Redirect to the enhanced chat functionality
+ this.sendMessage();
+ }
+
+ addChatMessage(message, sender) {
+ // Redirect to the enhanced chat message functionality
+ this.addMessageToChat(message, sender);
+ }
+
+ // Utility functions
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ truncateText(text, maxLength) {
+ if (text.length <= maxLength) return text;
+ return text.substr(0, maxLength) + '...';
+ }
+
+ // Enhanced UI Methods (Legacy - now handled by setupEnhancedChat)
+ setupChatInput() {
+ // This is now handled by the enhanced chat setup
+ this.setupEnhancedChat();
+ }
+
+ setupSearchSuggestions() {
+ const searchInput = document.getElementById('searchInput');
+ const suggestions = document.getElementById('searchSuggestions');
+
+ // Initialize enhanced search features
+ this.initializeQuickSearchCards();
+ this.initializeSearchTips();
+ this.initializeRecentSearches();
+ this.initializeAdvancedFilters();
+
+ if (searchInput && suggestions) {
+ searchInput.addEventListener('input', (e) => this.handleSearchInput(e));
+ searchInput.addEventListener('focus', () => this.showSearchSuggestions());
+ searchInput.addEventListener('blur', () => this.hideSearchSuggestions());
+
+ // Handle suggestion clicks
+ suggestions.querySelectorAll('.suggestion-item').forEach(item => {
+ item.addEventListener('click', () => {
+ const query = item.dataset.query;
+ if (query) {
+ searchInput.value = query;
+ suggestions.classList.remove('show');
+ this.searchPapers();
+ this.addToRecentSearches(query);
+ }
+ });
+ });
+ }
+ }
+
+ initializeQuickSearchCards() {
+ const quickSearchCards = document.querySelectorAll('.quick-search-card');
+ quickSearchCards.forEach(card => {
+ card.addEventListener('click', () => {
+ const query = card.dataset.query;
+ if (query) {
+ const searchInput = document.getElementById('searchInput');
+ if (searchInput) {
+ searchInput.value = query;
+ this.searchPapers();
+ this.addToRecentSearches(query);
+ }
+ }
+ });
+ });
+ }
+
+ initializeSearchTips() {
+ const tipsToggle = document.querySelector('.tips-toggle');
+ const tipsContent = document.querySelector('.tips-content');
+
+ if (tipsToggle && tipsContent) {
+ tipsToggle.addEventListener('click', () => {
+ tipsContent.classList.toggle('show');
+ const icon = tipsToggle.querySelector('i');
+ if (icon) {
+ icon.classList.toggle('fa-chevron-down');
+ icon.classList.toggle('fa-chevron-up');
+ }
+ });
+ }
+ }
+
+ initializeRecentSearches() {
+ this.loadRecentSearches();
+ }
+
+ initializeAdvancedFilters() {
+ // Advanced filters toggle is handled by global function
+ }
+
+ handleSearchInput(e) {
+ const query = e.target.value.trim();
+
+ if (query.length > 2) {
+ this.updateSearchSuggestions(query);
+ this.showSearchSuggestions();
+ } else if (query.length === 0) {
+ this.showSearchSuggestions();
+ } else {
+ this.hideSearchSuggestions();
+ }
+ }
+
+ updateSearchSuggestions(query) {
+ const searchSuggestions = document.getElementById('searchSuggestions');
+ if (!searchSuggestions) return;
+
+ // Generate dynamic suggestions based on the query
+ const suggestions = [
+ { text: query, count: '~1.2k papers' },
+ { text: query + ' applications', count: '~800 papers' },
+ { text: query + ' algorithms', count: '~650 papers' },
+ { text: query + ' recent advances', count: '~420 papers' }
+ ];
+
+ const suggestionsSection = searchSuggestions.querySelector('.suggestions-section');
+ if (suggestionsSection) {
+ const suggestionItems = suggestions.map(s => `
+
+
+ ${s.text}
+ ${s.count}
+
+ `).join('');
+
+ suggestionsSection.innerHTML = `
+ Suggestions
+ ${suggestionItems}
+ `;
+
+ // Re-attach event listeners
+ suggestionsSection.querySelectorAll('.suggestion-item').forEach(item => {
+ item.addEventListener('click', () => {
+ const query = item.dataset.query;
+ if (query) {
+ const searchInput = document.getElementById('searchInput');
+ if (searchInput) {
+ searchInput.value = query;
+ this.searchPapers();
+ this.hideSearchSuggestions();
+ this.addToRecentSearches(query);
+ }
+ }
+ });
+ });
+ }
+ }
+
+ showSearchSuggestions() {
+ const searchSuggestions = document.getElementById('searchSuggestions');
+ if (searchSuggestions) {
+ searchSuggestions.classList.add('show');
+ }
+ }
+
+ hideSearchSuggestions() {
+ setTimeout(() => {
+ const searchSuggestions = document.getElementById('searchSuggestions');
+ if (searchSuggestions) {
+ searchSuggestions.classList.remove('show');
+ }
+ }, 200);
+ }
+
+ addToRecentSearches(query) {
+ let recentSearches = JSON.parse(localStorage.getItem('recentSearches') || '[]');
+
+ // Remove if already exists
+ recentSearches = recentSearches.filter(search => search !== query);
+
+ // Add to beginning
+ recentSearches.unshift(query);
+
+ // Keep only last 5
+ recentSearches = recentSearches.slice(0, 5);
+
+ localStorage.setItem('recentSearches', JSON.stringify(recentSearches));
+ this.updateRecentSearchesDisplay();
+ }
+
+ loadRecentSearches() {
+ const recentSearches = JSON.parse(localStorage.getItem('recentSearches') || '[]');
+ if (recentSearches.length > 0) {
+ this.updateRecentSearchesDisplay();
+ }
+ }
+
+ updateRecentSearchesDisplay() {
+ const recentSearches = JSON.parse(localStorage.getItem('recentSearches') || '[]');
+ const recentSearchesContainer = document.getElementById('recentSearches');
+ const recentSearchItems = document.getElementById('recentSearchItems');
+
+ if (recentSearches.length > 0 && recentSearchesContainer && recentSearchItems) {
+ const recentHTML = recentSearches.map(search => `
+
+
+ ${search}
+
+ `).join('');
+
+ recentSearchItems.innerHTML = recentHTML;
+ recentSearchesContainer.style.display = 'block';
+
+ // Attach event listeners
+ recentSearchItems.querySelectorAll('.suggestion-item').forEach(item => {
+ item.addEventListener('click', () => {
+ const query = item.dataset.query;
+ if (query) {
+ const searchInput = document.getElementById('searchInput');
+ if (searchInput) {
+ searchInput.value = query;
+ this.searchPapers();
+ }
+ }
+ });
+ });
+ }
+ }
+
+ updateStatusIndicator() {
+ const indicator = document.getElementById('statusIndicator');
+ const chatStatus = document.getElementById('chatStatus');
+
+ if (indicator) {
+ const statusText = indicator.querySelector('.status-text');
+ const statusDot = indicator.querySelector('.status-dot');
+
+ if (this.currentDocumentId) {
+ statusText.textContent = 'Document Active';
+ statusDot.style.background = 'var(--success-color)';
+ } else {
+ statusText.textContent = 'Ready';
+ statusDot.style.background = 'var(--warning-color)';
+ }
+ }
+
+ if (chatStatus) {
+ const statusIndicator = chatStatus.querySelector('.status-indicator');
+ if (this.currentDocumentId) {
+ statusIndicator.classList.remove('offline');
+ statusIndicator.classList.add('online');
+ statusIndicator.querySelector('span').textContent = 'Document loaded';
+ } else {
+ statusIndicator.classList.remove('online');
+ statusIndicator.classList.add('offline');
+ statusIndicator.querySelector('span').textContent = 'No document selected';
+ }
+ }
+ }
+
+ updateUploadProgress(percentage, step) {
+ const progressContainer = document.getElementById('uploadProgress');
+ const progressFill = document.getElementById('progressFill');
+ const progressPercentage = document.getElementById('progressPercentage');
+ const progressSubtitle = document.getElementById('progressSubtitle');
+ const progressTime = document.getElementById('progressTime');
+
+ if (progressContainer && progressFill && progressPercentage) {
+ progressContainer.style.display = 'block';
+ progressFill.style.width = `${percentage}%`;
+ progressPercentage.textContent = `${percentage}%`;
+
+ // Update subtitle and time estimate
+ const subtitles = [
+ 'Preparing your document for analysis...',
+ 'Uploading your document securely...',
+ 'Extracting text and content...',
+ 'AI analyzing document structure...',
+ 'Analysis complete! Ready for questions.'
+ ];
+
+ if (progressSubtitle && step <= subtitles.length) {
+ progressSubtitle.textContent = subtitles[step - 1] || subtitles[0];
+ }
+
+ if (progressTime) {
+ if (percentage < 100) {
+ const remainingTime = Math.max(1, Math.round((100 - percentage) / 10));
+ progressTime.textContent = `~${remainingTime}s remaining`;
+ } else {
+ progressTime.textContent = 'Complete!';
+ }
+ }
+
+ // Update steps with enhanced system
+ const steps = progressContainer.querySelectorAll('.progress-step');
+ steps.forEach((stepEl, index) => {
+ const stepNumber = parseInt(stepEl.dataset.step);
+ if (stepNumber <= step) {
+ stepEl.classList.add('active');
+ } else {
+ stepEl.classList.remove('active');
+ }
+ });
+
+ if (percentage === 100) {
+ setTimeout(() => {
+ progressContainer.style.display = 'none';
+ }, 3000);
+ }
+ }
+ }
+
+ autoResizeTextarea(event) {
+ const textarea = event.target;
+ textarea.style.height = 'auto';
+ const newHeight = Math.min(textarea.scrollHeight, 120);
+ textarea.style.height = newHeight + 'px';
+ }
+
+ clearChat() {
+ const chatMessages = document.getElementById('chatMessages');
+ if (chatMessages) {
+ // Keep the welcome message
+ const welcomeMessage = chatMessages.querySelector('.welcome-message');
+ chatMessages.innerHTML = '';
+ if (welcomeMessage) {
+ chatMessages.appendChild(welcomeMessage);
+ }
+ }
+ this.chatHistory = [];
+ this.showToast('Chat cleared', 'success');
+ }
+
+ setActiveDocument(docId) {
+ this.currentDocumentId = docId;
+ this.updateStatusIndicator();
+
+ // Show chat indicator
+ const chatIndicator = document.getElementById('chatIndicator');
+ if (chatIndicator) {
+ chatIndicator.classList.add('active');
+ }
+ }
+
+ // Override the original methods to include enhanced functionality
+ async processFile(file) {
+ if (!file || !this.allowed_file(file.name)) {
+ this.showToast('Please select a valid file (PDF, TXT, or DOCX)', 'error');
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append('file', file);
+
+ this.showLoading(true);
+ this.updateUploadProgress(25, 1);
+
+ try {
+ this.updateUploadProgress(50, 2);
+
+ const response = await fetch('/upload', {
+ method: 'POST',
+ body: formData
+ });
+
+ this.updateUploadProgress(75, 3);
+
+ const data = await response.json();
+
+ if (response.ok) {
+ this.updateUploadProgress(100, 4);
+ this.displayUploadResult(data);
+ this.setActiveDocument(data.doc_id);
+ this.showToast('File uploaded and analyzed successfully!', 'success');
+ } else {
+ throw new Error(data.error || 'Upload failed');
+ }
+ } catch (error) {
+ console.error('Upload error:', error);
+ this.showToast(error.message, 'error');
+ this.updateUploadProgress(0, 0);
+ } finally {
+ this.showLoading(false);
+ }
+ }
+
+ allowed_file(filename) {
+ const allowedExtensions = ['pdf', 'txt', 'docx'];
+ const extension = filename.split('.').pop().toLowerCase();
+ return allowedExtensions.includes(extension);
+ }
+
+ // Page Navigation Methods
+ setupPageNavigation() {
+ // Handle smooth scrolling for landing page links
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
+ anchor.addEventListener('click', function (e) {
+ e.preventDefault();
+ const target = document.querySelector(this.getAttribute('href'));
+ if (target && target.closest('#landingPage')) {
+ target.scrollIntoView({
+ behavior: 'smooth',
+ block: 'start'
+ });
+ }
+ });
+ });
+ }
+
+ setupMobileNav() {
+ // Toggle landing nav links
+ document.querySelectorAll('.navbar .mobile-nav-toggle').forEach(toggle => {
+ toggle.addEventListener('click', () => {
+ const container = toggle.closest('.navbar').querySelector('.landing-nav-links, .nav-links');
+ if (container) {
+ container.classList.toggle('show');
+ }
+ });
+ });
+ // Close menu on link click (mobile)
+ document.querySelectorAll('.landing-nav-links .nav-link').forEach(link => {
+ link.addEventListener('click', () => {
+ const links = document.querySelector('.landing-nav-links');
+ links && links.classList.remove('show');
+ });
+ });
+ }
+
+ setupVhUnit() {
+ const setVh = () => {
+ const vh = window.innerHeight * 0.01;
+ document.documentElement.style.setProperty('--vh', `${vh}px`);
+ };
+ setVh();
+ window.addEventListener('resize', setVh);
+ window.addEventListener('orientationchange', setVh);
+ }
+
+ // Removed split-mode; tabs-only behavior
+ setupViewToggle() {
+ // Strict tabs: only one panel visible
+ document.querySelectorAll('.tab-btn').forEach(btn => {
+ btn.classList.remove('active');
+ });
+ document.querySelectorAll('.tab-content').forEach(content => {
+ content.classList.remove('active');
+ });
+ document.querySelector(`[data-tab="summary"]`)?.classList.add('active');
+ document.getElementById('summary-tab')?.classList.add('active');
+ history.replaceState(null, null, `#summary`);
+ const tabDisplayName = 'Summary';
+ showToast(`Focused ${tabDisplayName}`, 'info');
+ }
+
+ navigateToApp(section = 'search') {
+ // Hide landing page
+ const landingPage = document.getElementById('landingPage');
+ const appPage = document.getElementById('appPage');
+
+ if (landingPage && appPage) {
+ landingPage.classList.remove('active');
+ appPage.classList.add('active');
+
+ this.currentPage = 'app';
+
+ // Switch to the specified section
+ this.switchSection(section);
+
+ // Show toast message
+ this.showToast(`Welcome to Research Radar! ${section === 'search' ? 'Start searching for papers' : 'Upload your documents'}`, 'success');
+
+ // Focus on the relevant input
+ setTimeout(() => {
+ if (section === 'search') {
+ const searchInput = document.getElementById('searchInput');
+ if (searchInput) searchInput.focus();
+ } else if (section === 'upload') {
+ // Auto-focus on upload section
+ document.getElementById('upload').scrollIntoView({ behavior: 'smooth' });
+ }
+ }, 500);
+ }
+ }
+
+ navigateToLanding() {
+ const landingPage = document.getElementById('landingPage');
+ const appPage = document.getElementById('appPage');
+
+ if (landingPage && appPage) {
+ appPage.classList.remove('active');
+ landingPage.classList.add('active');
+
+ this.currentPage = 'landing';
+
+ // Scroll to top
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+
+ this.showToast('Welcome back to the homepage!', 'info');
+ }
+ }
+
+ // Enhanced section switching for app page
+ switchSection(sectionName) {
+ if (this.currentPage !== 'app') return;
+
+ // Update navigation
+ document.querySelectorAll('.app-nav .nav-link').forEach(link => {
+ link.classList.remove('active');
+ });
+
+ const activeLink = document.querySelector(`.app-nav [data-section="${sectionName}"]`);
+ if (activeLink) {
+ activeLink.classList.add('active');
+ }
+
+ // Update sections
+ document.querySelectorAll('#appPage .section').forEach(section => {
+ section.classList.remove('active');
+ });
+
+ const targetSection = document.getElementById(sectionName);
+ if (targetSection) {
+ targetSection.classList.add('active');
+ }
+
+ this.currentSection = sectionName;
+
+ // Add section-specific functionality
+ this.onSectionChange(sectionName);
+ }
+
+ onSectionChange(sectionName) {
+ switch(sectionName) {
+ case 'search':
+ // Focus search input after animation
+ setTimeout(() => {
+ const searchInput = document.getElementById('searchInput');
+ if (searchInput) searchInput.focus();
+ }, 300);
+ break;
+ case 'upload':
+ // Reset upload progress
+ this.updateUploadProgress(0, 0);
+ break;
+ case 'mypapers':
+ // Load papers when section is accessed
+ this.loadMyPapers();
+ break;
+ case 'chat':
+ // Focus chat input
+ setTimeout(() => {
+ const chatInput = document.getElementById('chatInput');
+ if (chatInput) chatInput.focus();
+ }, 300);
+ break;
+ }
+ }
+
+ // Enhanced Chat Functionality
+ setupEnhancedChat() {
+ this.messageCount = 0;
+ this.sessionStartTime = Date.now();
+ this.updateChatStats();
+ this.setupChatSuggestions();
+ this.setupChatInput();
+ this.setupQuickActions();
+ this.startSessionTimer();
+ }
+
+ setupChatSuggestions() {
+ // Handle suggestion chips
+ document.querySelectorAll('.suggestion-chip-enhanced').forEach(chip => {
+ chip.addEventListener('click', (e) => {
+ const question = e.currentTarget.dataset.question;
+ if (question) {
+ const chatInput = document.getElementById('chatInput');
+ chatInput.value = question;
+ chatInput.focus();
+ this.autoResizeTextarea(chatInput);
+ }
+ });
+ });
+ }
+
+ setupChatInput() {
+ const chatInput = document.getElementById('chatInput');
+ const sendBtn = document.getElementById('chatSendBtn');
+ const clearBtn = document.getElementById('chatClearBtn');
+
+ // Auto-resize textarea
+ chatInput.addEventListener('input', () => {
+ this.autoResizeTextarea(chatInput);
+ });
+
+ // Handle Enter key
+ chatInput.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ this.sendMessage();
+ } else if (e.key === 'Enter' && e.shiftKey) {
+ // Allow new line
+ }
+ });
+
+ // Send button
+ sendBtn.addEventListener('click', () => {
+ this.sendMessage();
+ });
+
+ // Clear chat
+ clearBtn.addEventListener('click', () => {
+ this.clearChat();
+ });
+
+ // Attach button (placeholder)
+ document.getElementById('attachBtn')?.addEventListener('click', () => {
+ this.showNotification('File attachment coming soon!', 'info');
+ });
+
+ // Emoji button (placeholder)
+ document.getElementById('emojiBtn')?.addEventListener('click', () => {
+ this.showNotification('Emoji picker coming soon!', 'info');
+ });
+ }
+
+ setupQuickActions() {
+ // Quick action buttons in status card
+ document.querySelectorAll('.quick-action-btn').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ });
+ });
+ }
+
+ autoResizeTextarea(textarea) {
+ textarea.style.height = 'auto';
+ textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
+ }
+
+ sendMessage() {
+ const chatInput = document.getElementById('chatInput');
+ const message = chatInput.value.trim();
+
+ if (!message) return;
+
+ // Add user message to chat
+ this.addMessageToChat(message, 'user');
+
+ // Clear input
+ chatInput.value = '';
+ this.autoResizeTextarea(chatInput);
+
+ // Show typing indicator
+ this.showTypingIndicator();
+
+ // Update stats
+ this.messageCount++;
+ this.updateChatStats();
+
+ // Send to backend
+ this.sendChatMessage(message);
+ }
+
+ addMessageToChat(message, sender = 'user') {
+ const chatMessages = document.getElementById('chatMessages');
+ const messageDiv = document.createElement('div');
+
+ const currentTime = new Date().toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+
+ if (sender === 'user') {
+ messageDiv.className = 'chat-message user-message';
+ messageDiv.innerHTML = `
+
+
+
+
+
${this.escapeHtml(message)}
+
+
+ `;
+ } else {
+ messageDiv.className = 'chat-message bot-message';
+ messageDiv.innerHTML = `
+
+
+ `;
+ }
+
+ // Remove welcome message if it's the first user message
+ const welcomeMessage = chatMessages.querySelector('.welcome-message-enhanced');
+ if (welcomeMessage && sender === 'user') {
+ welcomeMessage.style.animation = 'fadeOut 0.3s ease-out forwards';
+ setTimeout(() => {
+ welcomeMessage.remove();
+ }, 300);
+ }
+
+ chatMessages.appendChild(messageDiv);
+
+ // Scroll to bottom
+ const container = document.querySelector('.chat-messages-container');
+ container.scrollTop = container.scrollHeight;
+
+ // Update chat status
+ this.updateChatStatus('active');
+ }
+
+ showTypingIndicator() {
+ const loadingIndicator = document.getElementById('chatLoading');
+ loadingIndicator.style.display = 'flex';
+
+ // Scroll to show typing indicator
+ const container = document.querySelector('.chat-messages-container');
+ setTimeout(() => {
+ container.scrollTop = container.scrollHeight;
+ }, 100);
+ }
+
+ hideTypingIndicator() {
+ const loadingIndicator = document.getElementById('chatLoading');
+ loadingIndicator.style.display = 'none';
+ }
+
+ clearChat() {
+ if (confirm('Are you sure you want to clear the chat history?')) {
+ const chatMessages = document.getElementById('chatMessages');
+ chatMessages.innerHTML = `
+
+
+
+
+
+
+
👋 Welcome back to Research Radar!
+
Chat cleared. I'm ready to help you with your research questions.
+
+
+
+
+ `;
+
+ this.messageCount = 0;
+ this.updateChatStats();
+ this.updateChatStatus('ready');
+ this.showNotification('Chat cleared successfully', 'success');
+ }
+ }
+
+ updateChatStats() {
+ const messageCountEl = document.getElementById('messageCount');
+ const sessionTimeEl = document.getElementById('sessionTime');
+
+ if (messageCountEl) {
+ messageCountEl.textContent = this.messageCount;
+ }
+ }
+
+ startSessionTimer() {
+ setInterval(() => {
+ const sessionTimeEl = document.getElementById('sessionTime');
+ if (sessionTimeEl) {
+ const elapsed = Math.floor((Date.now() - this.sessionStartTime) / 1000);
+ const minutes = Math.floor(elapsed / 60);
+ const seconds = elapsed % 60;
+ sessionTimeEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
+ }
+ }, 1000);
+ }
+
+ updateChatStatus(status) {
+ const statusTitle = document.getElementById('statusTitle');
+ const statusDescription = document.getElementById('statusDescription');
+
+ switch (status) {
+ case 'ready':
+ statusTitle.textContent = 'Ready to Chat';
+ statusDescription.textContent = 'Upload a document or search for papers to get started';
+ break;
+ case 'active':
+ statusTitle.textContent = 'Chat Active';
+ statusDescription.textContent = 'AI assistant is ready to answer your questions';
+ break;
+ case 'processing':
+ statusTitle.textContent = 'Processing...';
+ statusDescription.textContent = 'AI is analyzing your question';
+ break;
+ }
+ }
+
+ sendChatMessage(message) {
+ this.updateChatStatus('processing');
+
+ fetch('/chat', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ message: message })
+ })
+ .then(response => response.json())
+ .then(data => {
+ this.hideTypingIndicator();
+
+ if (data.success) {
+ this.addMessageToChat(data.response, 'bot');
+ this.messageCount++;
+ this.updateChatStats();
+ } else {
+ this.addMessageToChat(
+ `I apologize, but I encountered an error: ${data.error || 'Unknown error'}. Please try again.`,
+ 'bot'
+ );
+ }
+
+ this.updateChatStatus('active');
+ })
+ .catch(error => {
+ console.error('Chat error:', error);
+ this.hideTypingIndicator();
+ this.addMessageToChat(
+ 'I apologize, but I\'m having trouble connecting right now. Please check your connection and try again.',
+ 'bot'
+ );
+ this.updateChatStatus('ready');
+ });
+ }
+
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ // Global functions for HTML onclick handlers
+ toggleSuggestions() {
+ const content = document.getElementById('suggestionsContent');
+ const toggle = document.querySelector('.suggestions-toggle i');
+
+ if (content.classList.contains('collapsed')) {
+ content.classList.remove('collapsed');
+ toggle.className = 'fas fa-chevron-up';
+ } else {
+ content.classList.add('collapsed');
+ toggle.className = 'fas fa-chevron-down';
+ }
+ }
+
+ showQuickActions() {
+ const quickActions = document.getElementById('chatQuickActions');
+ quickActions.style.display = 'block';
+ quickActions.style.animation = 'slideUp 0.3s ease-out';
+ }
+
+ hideQuickActions() {
+ const quickActions = document.getElementById('chatQuickActions');
+ quickActions.style.animation = 'slideDown 0.3s ease-out';
+ setTimeout(() => {
+ quickActions.style.display = 'none';
+ }, 300);
+ }
+
+ generateSummary() {
+ this.addMessageToChat('Please generate a summary of the current document.', 'user');
+ this.sendChatMessage('Please generate a summary of the current document.');
+ this.hideQuickActions();
+ }
+
+ extractKeyPoints() {
+ this.addMessageToChat('What are the key points from this paper?', 'user');
+ this.sendChatMessage('What are the key points from this paper?');
+ this.hideQuickActions();
+ }
+
+ findRelatedPapers() {
+ this.addMessageToChat('Can you suggest related papers to this research?', 'user');
+ this.sendChatMessage('Can you suggest related papers to this research?');
+ this.hideQuickActions();
+ }
+
+ exportChat() {
+ // Get all messages
+ const messages = document.querySelectorAll('.chat-message');
+ let chatText = 'Research Radar Chat Export\n';
+ chatText += '='.repeat(50) + '\n\n';
+
+ messages.forEach(message => {
+ const sender = message.querySelector('.message-sender')?.textContent || 'Unknown';
+ const time = message.querySelector('.message-time')?.textContent || '';
+ const text = message.querySelector('.message-text')?.textContent || '';
+
+ if (text.trim()) {
+ chatText += `[${time}] ${sender}:\n${text.trim()}\n\n`;
+ }
+ });
+
+ // Create and download file
+ const blob = new Blob([chatText], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `research-radar-chat-${new Date().toISOString().split('T')[0]}.txt`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+
+ this.showNotification('Chat exported successfully!', 'success');
+ this.hideQuickActions();
+ }
+
+ // Summary + Chat Functionality
+ openSummaryChat(paperData, summaryText) {
+ console.log('openSummaryChat called with:', { paperData, summaryText });
+
+ // Hide current section and show summary-chat
+ document.querySelectorAll('.section').forEach(section => {
+ section.style.display = 'none';
+ });
+
+ const summarySection = document.getElementById('summary-chat');
+ if (!summarySection) {
+ console.error('summary-chat section not found!');
+ this.showToast('Error: Summary section not found', 'error');
+ return;
+ }
+ summarySection.style.display = 'block';
+
+ // Update paper information
+ document.getElementById('paperTitle').textContent = paperData.title || 'Research Paper';
+ document.getElementById('paperAuthor').textContent = paperData.authors ? paperData.authors.join(', ') : 'Unknown Author';
+ document.getElementById('paperDate').textContent = paperData.published || new Date().getFullYear();
+ document.getElementById('paperCategory').textContent = paperData.category || 'Research';
+
+ // Show summary
+ this.displaySummaryInPanel(summaryText);
+
+ // Setup chat panel
+ this.setupChatPanel();
+
+ // Store current paper data for chat context
+ this.currentPaper = paperData;
+
+ // Default to Chat tab for immediate Q&A after tabs are in DOM
+ setTimeout(() => {
+ try { switchTab('chat'); } catch (_) {}
+ const chatInput = document.getElementById('chatInputPanel');
+ if (chatInput) chatInput.focus();
+ }, 150);
+ }
+
+ displaySummaryInPanel(summaryText) {
+ const summaryLoading = document.getElementById('summaryLoading');
+ const summaryTextEl = document.getElementById('summaryText');
+
+ // Hide loading and show summary
+ summaryLoading.style.display = 'none';
+ summaryTextEl.style.display = 'block';
+ summaryTextEl.innerHTML = this.formatSummaryText(summaryText);
+
+ // Update stats
+ this.updateSummaryStats(summaryText);
+ }
+
+ formatSummaryText(text) {
+ // Convert plain text to formatted HTML
+ return text
+ .replace(/\n\n/g, '')
+ .replace(/\n/g, '
')
+ .replace(/\*\*(.*?)\*\*/g, '$1')
+ .replace(/\*(.*?)\*/g, '$1')
+ .replace(/^/, '
')
+ .replace(/$/, '
');
+ }
+
+ updateSummaryStats(text) {
+ const wordCount = text.split(/\s+/).length;
+ const readingTime = Math.ceil(wordCount / 200); // Average reading speed
+ const compressionRatio = Math.round((1 - (text.length / (text.length * 3))) * 100); // Estimate
+
+ const wc = document.getElementById('wordCount');
+ const rt = document.getElementById('readingTime');
+ const cr = document.getElementById('compressionRatio');
+ if (wc) wc.textContent = wordCount.toLocaleString();
+ if (rt) rt.textContent = `${readingTime} min`;
+ if (cr) cr.textContent = `${compressionRatio}%`;
+ }
+
+ setupChatPanel() {
+ const chatInput = document.getElementById('chatInputPanel');
+ const sendBtn = document.getElementById('chatSendBtnPanel');
+
+ if (!chatInput || !sendBtn) return;
+
+ // Clear any existing event listeners by cloning
+ const newChatInput = chatInput.cloneNode(true);
+ chatInput.parentNode.replaceChild(newChatInput, chatInput);
+
+ const newSendBtn = sendBtn.cloneNode(true);
+ sendBtn.parentNode.replaceChild(newSendBtn, sendBtn);
+
+ // Add new event listeners
+ newChatInput.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ this.sendChatMessagePanel();
+ }
+ });
+
+ newChatInput.addEventListener('input', () => {
+ this.autoResizeTextarea(newChatInput);
+ });
+
+ newSendBtn.addEventListener('click', () => {
+ this.sendChatMessagePanel();
+ });
+ }
+
+ sendChatMessagePanel() {
+ const chatInput = document.getElementById('chatInputPanel');
+ const message = chatInput.value.trim();
+
+ if (!message) return;
+
+ // Add user message
+ this.addChatMessagePanel(message, 'user');
+
+ // Clear input
+ chatInput.value = '';
+ this.autoResizeTextarea(chatInput);
+
+ // Show typing indicator and send to backend
+ this.showChatTypingPanel();
+ this.sendChatToBackend(message);
+ }
+
+ addChatMessagePanel(message, sender) {
+ const chatPanel = document.getElementById('chatMessagesPanel');
+
+ // Remove welcome message if it exists
+ const welcome = chatPanel.querySelector('.chat-welcome');
+ if (welcome && sender === 'user') {
+ welcome.remove();
+ }
+
+ const messageDiv = document.createElement('div');
+ messageDiv.className = `chat-message-panel ${sender}`;
+
+ const currentTime = new Date().toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+
+ messageDiv.innerHTML = `
+
+
+
+
+
+ ${sender === 'bot' ? message : this.escapeHtml(message)}
+
+
${currentTime}
+
+ `;
+
+ chatPanel.appendChild(messageDiv);
+ chatPanel.scrollTop = chatPanel.scrollHeight;
+ }
+
+ showChatTypingPanel() {
+ const chatPanel = document.getElementById('chatMessagesPanel');
+
+ const typingDiv = document.createElement('div');
+ typingDiv.className = 'chat-message-panel bot';
+ typingDiv.id = 'typingIndicatorPanel';
+
+ typingDiv.innerHTML = `
+
+
+
+
+ `;
+
+ chatPanel.appendChild(typingDiv);
+ chatPanel.scrollTop = chatPanel.scrollHeight;
+ }
+
+ hideChatTypingPanel() {
+ const typingIndicator = document.getElementById('typingIndicatorPanel');
+ if (typingIndicator) {
+ typingIndicator.remove();
+ }
+ }
+
+ sendChatToBackend(message) {
+ fetch('/chat', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ message: message })
+ })
+ .then(response => response.json())
+ .then(data => {
+ this.hideChatTypingPanel();
+
+ if (data.success) {
+ this.addChatMessagePanel(data.response, 'bot');
+ } else {
+ this.addChatMessagePanel(
+ `I apologize, but I encountered an error: ${data.error || 'Unknown error'}. Please try again.`,
+ 'bot'
+ );
+ }
+ })
+ .catch(error => {
+ console.error('Chat error:', error);
+ this.hideChatTypingPanel();
+ this.addChatMessagePanel(
+ 'I apologize, but I\'m having trouble connecting right now. Please check your connection and try again.',
+ 'bot'
+ );
+ });
+ }
+
+ // My Papers functionality
+ async loadMyPapers() {
+ const loadingEl = document.getElementById('mypapersLoading');
+ const emptyEl = document.getElementById('mypapersEmpty');
+ const gridEl = document.getElementById('papersGrid');
+
+ if (loadingEl) loadingEl.style.display = 'block';
+ if (emptyEl) emptyEl.style.display = 'none';
+ if (gridEl) gridEl.innerHTML = '';
+
+ try {
+ const response = await fetch('/documents');
+ const data = await response.json();
+
+ if (response.ok && data.success) {
+ this.displayMyPapers(data.documents);
+ } else {
+ throw new Error(data.error || 'Failed to load papers');
+ }
+ } catch (error) {
+ console.error('Error loading papers:', error);
+ this.showToast('Failed to load papers', 'error');
+ if (emptyEl) emptyEl.style.display = 'block';
+ } finally {
+ if (loadingEl) loadingEl.style.display = 'none';
+ }
+ }
+
+ displayMyPapers(documents) {
+ const emptyEl = document.getElementById('mypapersEmpty');
+ const gridEl = document.getElementById('papersGrid');
+
+ if (!documents || documents.length === 0) {
+ if (emptyEl) emptyEl.style.display = 'block';
+ if (gridEl) gridEl.innerHTML = '';
+ return;
+ }
+
+ if (emptyEl) emptyEl.style.display = 'none';
+ if (!gridEl) return;
+
+ gridEl.innerHTML = documents.map(doc => this.createPaperCard(doc)).join('');
+
+ // Add event listeners to the buttons after creating them
+ this.setupMyPapersButtons();
+ }
+
+ setupMyPapersButtons() {
+ console.log('Setting up My Papers buttons...');
+
+ // Add event listeners to Open buttons
+ const openButtons = document.querySelectorAll('.paper-action-btn.primary');
+ console.log(`Found ${openButtons.length} Open buttons`);
+
+ openButtons.forEach(button => {
+ button.addEventListener('click', (e) => {
+ e.preventDefault();
+ const docId = button.getAttribute('data-doc-id');
+ console.log('Open button clicked for docId:', docId);
+ this.openPaperFromMyPapers(docId);
+ });
+ });
+
+ // Add event listeners to Delete buttons
+ const deleteButtons = document.querySelectorAll('.paper-action-btn.secondary');
+ console.log(`Found ${deleteButtons.length} Delete buttons`);
+
+ deleteButtons.forEach(button => {
+ button.addEventListener('click', (e) => {
+ e.preventDefault();
+ const docId = button.getAttribute('data-doc-id');
+ console.log('Delete button clicked for docId:', docId);
+ this.deletePaper(docId);
+ });
+ });
+ }
+
+ createPaperCard(doc) {
+ const icon = this.getDocumentIcon(doc.type);
+ const authors = Array.isArray(doc.authors) ? doc.authors.join(', ') : doc.authors || 'Unknown';
+ const date = doc.upload_date || doc.published || 'Unknown Date';
+
+ return `
+
+
+
+
+
+
+
+ `;
+ }
+
+ getDocumentIcon(type) {
+ switch (type) {
+ case 'arxiv_paper': return 'fas fa-graduation-cap';
+ case 'uploaded_document': return 'fas fa-file-upload';
+ default: return 'fas fa-file-alt';
+ }
+ }
+
+ async openPaperFromMyPapers(docId) {
+ console.log('ResearchRadar.openPaperFromMyPapers called with docId:', docId);
+ try {
+ // Set the document as active for chat
+ this.currentDocumentId = docId;
+
+ // Fetch document summary
+ console.log('Fetching document summary...');
+ const response = await fetch(`/documents/${docId}/summary`);
+ const data = await response.json();
+
+ console.log('Summary response:', data);
+
+ if (response.ok && data.success) {
+ // Activate the document for chat
+ await fetch(`/documents/${docId}/activate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ // Redirect to summary and chat page
+ console.log('Redirecting to summary and chat...');
+ this.openSummaryChat(data.document, data.summary);
+ this.showToast('Paper loaded successfully', 'success');
+ } else {
+ throw new Error(data.error || 'Failed to load paper');
+ }
+ } catch (error) {
+ console.error('Error opening paper:', error);
+ this.showToast('Failed to open paper', 'error');
+ }
+ }
+
+ async deletePaper(docId) {
+ console.log('ResearchRadar.deletePaper called with docId:', docId);
+ if (!confirm('Are you sure you want to delete this paper? This action cannot be undone.')) {
+ return;
+ }
+
+ try {
+ console.log('Deleting document...');
+ const response = await fetch(`/documents/${docId}`, {
+ method: 'DELETE',
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ const data = await response.json();
+ console.log('Delete response:', data);
+
+ if (response.ok && data.success) {
+ const card = document.querySelector(`[data-doc-id="${docId}"]`);
+ if (card) {
+ card.remove();
+ }
+ this.showToast('Paper deleted successfully', 'success');
+ } else {
+ throw new Error(data.error || 'Failed to delete paper');
+ }
+ } catch (error) {
+ console.error('Error deleting paper:', error);
+ this.showToast('Failed to delete paper', 'error');
+ }
+ }
+
+ async clearAllPapers() {
+ if (!confirm('Are you sure you want to clear all papers? This action cannot be undone.')) {
+ return;
+ }
+
+ try {
+ const response = await fetch('/documents', {
+ method: 'DELETE',
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ const data = await response.json();
+
+ if (response.ok && data.success) {
+ const gridEl = document.getElementById('papersGrid');
+ if (gridEl) {
+ gridEl.innerHTML = '';
+ document.getElementById('mypapersEmpty').style.display = 'block';
+ }
+ this.showToast('All papers cleared successfully', 'success');
+ } else {
+ throw new Error(data.error || 'Failed to clear papers');
+ }
+ } catch (error) {
+ console.error('Error clearing papers:', error);
+ this.showToast('Failed to clear papers', 'error');
+ }
+ }
+}
+
+
+
+// Global navigation functions
+function navigateToApp(section = 'search') {
+ console.log(`Global navigateToApp called with section: ${section}`);
+
+ if (window.researchRadar) {
+ console.log('Using ResearchRadar instance');
+ window.researchRadar.navigateToApp(section);
+ } else {
+ console.log('ResearchRadar instance not ready, using fallback navigation');
+ // Fallback navigation if ResearchRadar isn't ready yet
+ const landingPage = document.getElementById('landingPage');
+ const appPage = document.getElementById('appPage');
+
+ if (landingPage && appPage) {
+ landingPage.classList.remove('active');
+ appPage.classList.add('active');
+
+ // Switch to the requested section
+ setTimeout(() => {
+ const sections = document.querySelectorAll('.section');
+ sections.forEach(s => s.classList.remove('active'));
+
+ const targetSection = document.getElementById(section);
+ if (targetSection) {
+ targetSection.classList.add('active');
+ }
+
+ // Update navigation
+ const navLinks = document.querySelectorAll('.nav-link');
+ navLinks.forEach(link => link.classList.remove('active'));
+
+ const activeNavLink = document.querySelector(`[data-section="${section}"]`);
+ if (activeNavLink) {
+ activeNavLink.classList.add('active');
+ }
+ }, 50);
+ }
+ }
+}
+
+function navigateToLanding() {
+ console.log('Global navigateToLanding called');
+
+ if (window.researchRadar) {
+ console.log('Using ResearchRadar instance');
+ window.researchRadar.navigateToLanding();
+ } else {
+ console.log('ResearchRadar instance not ready, using fallback navigation');
+ // Fallback navigation if ResearchRadar isn't ready yet
+ const landingPage = document.getElementById('landingPage');
+ const appPage = document.getElementById('appPage');
+
+ if (landingPage && appPage) {
+ appPage.classList.remove('active');
+ landingPage.classList.add('active');
+ }
+ }
+}
+
+// Global chat functions
+function toggleSuggestions() {
+ if (window.researchRadar) {
+ window.researchRadar.toggleSuggestions();
+ }
+}
+
+function showQuickActions() {
+ if (window.researchRadar) {
+ window.researchRadar.showQuickActions();
+ }
+}
+
+function hideQuickActions() {
+ if (window.researchRadar) {
+ window.researchRadar.hideQuickActions();
+ }
+}
+
+function generateSummary() {
+ if (window.researchRadar) {
+ window.researchRadar.generateSummary();
+ }
+}
+
+function extractKeyPoints() {
+ if (window.researchRadar) {
+ window.researchRadar.extractKeyPoints();
+ }
+}
+
+function findRelatedPapers() {
+ if (window.researchRadar) {
+ window.researchRadar.findRelatedPapers();
+ }
+}
+
+// Global functions for My Papers buttons
+function openPaperFromMyPapers(docId) {
+ console.log('Global openPaperFromMyPapers called with docId:', docId);
+
+ // Wait for ResearchRadar to be available
+ const waitForResearchRadar = () => {
+ if (window.researchRadar) {
+ window.researchRadar.openPaperFromMyPapers(docId);
+ } else {
+ console.log('ResearchRadar not ready, waiting...');
+ setTimeout(waitForResearchRadar, 100);
+ }
+ };
+
+ waitForResearchRadar();
+}
+
+function deletePaperFromMyPapers(docId) {
+ console.log('Global deletePaperFromMyPapers called with docId:', docId);
+
+ // Wait for ResearchRadar to be available
+ const waitForResearchRadar = () => {
+ if (window.researchRadar) {
+ window.researchRadar.deletePaper(docId);
+ } else {
+ console.log('ResearchRadar not ready, waiting...');
+ setTimeout(waitForResearchRadar, 100);
+ }
+ };
+
+ waitForResearchRadar();
+}
+
+function exportChat() {
+ if (window.researchRadar) {
+ window.researchRadar.exportChat();
+ }
+}
+
+// Enhanced Search Functions
+function toggleAdvancedSearch() {
+ const advancedFilters = document.getElementById('advancedFilters');
+ const toggleBtn = document.querySelector('.advanced-search-btn');
+
+ if (advancedFilters) {
+ const isShowing = advancedFilters.classList.toggle('show');
+
+ if (toggleBtn) {
+ const icon = toggleBtn.querySelector('i');
+ if (icon) {
+ if (isShowing) {
+ icon.classList.remove('fa-sliders-h');
+ icon.classList.add('fa-times');
+ toggleBtn.classList.add('active');
+ } else {
+ icon.classList.remove('fa-times');
+ icon.classList.add('fa-sliders-h');
+ toggleBtn.classList.remove('active');
+ }
+ }
+ }
+ }
+}
+
+function toggleSearchTips() {
+ const tipsContent = document.querySelector('.tips-content');
+ const tipsToggle = document.querySelector('.tips-toggle');
+
+ if (tipsContent) {
+ tipsContent.classList.toggle('show');
+
+ if (tipsToggle) {
+ const icon = tipsToggle.querySelector('i');
+ if (icon) {
+ icon.classList.toggle('fa-chevron-down');
+ icon.classList.toggle('fa-chevron-up');
+ }
+ }
+ }
+}
+
+function clearSearchHistory() {
+ localStorage.removeItem('recentSearches');
+ const recentSearchesContainer = document.getElementById('recentSearches');
+ if (recentSearchesContainer) {
+ recentSearchesContainer.style.display = 'none';
+ }
+
+ if (window.researchRadar) {
+ window.researchRadar.showToast('Search history cleared', 'success');
+ }
+}
+
+// Enhanced Upload Functions
+function toggleUploadTips() {
+ const tipsContent = document.getElementById('uploadTipsContent');
+ const tipsToggle = document.querySelector('.upload-tips .tips-toggle');
+
+ if (tipsContent) {
+ tipsContent.classList.toggle('show');
+
+ if (tipsToggle) {
+ const icon = tipsToggle.querySelector('i');
+ if (icon) {
+ icon.classList.toggle('fa-chevron-down');
+ icon.classList.toggle('fa-chevron-up');
+ }
+ }
+ }
+}
+
+// Additional missing functions for summary-chat functionality
+function goBackToSearch() {
+ console.log('Global goBackToSearch called');
+
+ if (window.researchRadar) {
+ console.log('Using ResearchRadar instance for goBackToSearch');
+
+ // Hide summary-chat section
+ const summarySection = document.getElementById('summary-chat');
+ if (summarySection) {
+ summarySection.classList.remove('active');
+ summarySection.style.display = 'none';
+ }
+
+ // Show search section and restore navigation
+ window.researchRadar.switchSection('search');
+
+ console.log('Successfully returned to search section');
+ } else {
+ console.log('ResearchRadar instance not ready, using fallback navigation');
+
+ // Fallback navigation
+ const summarySection = document.getElementById('summary-chat');
+ const searchSection = document.getElementById('search');
+
+ if (summarySection) {
+ summarySection.classList.remove('active');
+ summarySection.style.display = 'none';
+ }
+
+ if (searchSection) {
+ searchSection.classList.add('active');
+ searchSection.style.display = 'block';
+ }
+
+ // Update navigation
+ const navLinks = document.querySelectorAll('.nav-link');
+ navLinks.forEach(link => link.classList.remove('active'));
+
+ const searchNavLink = document.querySelector('[data-section="search"]');
+ if (searchNavLink) {
+ searchNavLink.classList.add('active');
+ }
+ }
+}
+
+function exportSummaryChat() {
+ console.log('Exporting summary and chat...');
+ if (window.researchRadar) {
+ window.researchRadar.showToast('Export feature coming soon!', 'info');
+ }
+}
+
+function shareSummary() {
+ console.log('Sharing summary...');
+ if (window.researchRadar) {
+ window.researchRadar.showToast('Share feature coming soon!', 'info');
+ }
+}
+
+function regenerateSummary() {
+ console.log('Regenerating summary...');
+ if (window.researchRadar) {
+ window.researchRadar.showToast('Regenerating summary...', 'info');
+ }
+}
+
+function copySummary() {
+ const summaryText = document.getElementById('summaryText');
+ if (summaryText) {
+ navigator.clipboard.writeText(summaryText.textContent).then(() => {
+ if (window.researchRadar) {
+ window.researchRadar.showToast('Summary copied to clipboard!', 'success');
+ }
+ });
+ }
+}
+
+function askQuickQuestion(question) {
+ const chatInput = document.getElementById('chatInputPanel');
+ if (chatInput) {
+ chatInput.value = question;
+ chatInput.focus();
+ }
+}
+
+// Global summarize paper function (fallback for any remaining onclick handlers)
+function summarizePaper(paperUrl) {
+ console.log(`Global summarizePaper called with URL: ${paperUrl}`);
+
+ if (window.researchRadar) {
+ console.log('Using ResearchRadar instance for summarizePaper');
+ window.researchRadar.summarizePaper(paperUrl);
+ } else {
+ console.error('ResearchRadar instance not available for summarizePaper');
+ alert('Application not ready. Please try again in a moment.');
+ }
+}
+
+// Immediate setup for critical buttons (before full initialization)
+function setupCriticalButtons() {
+ console.log('Setting up critical buttons immediately...');
+
+ // Set up the main navigation buttons with fallback functions
+ const buttons = [
+ { selector: '.nav-cta-btn', action: () => navigateToApp('search'), name: 'Get Started' },
+ { selector: '.cta-button.primary', action: () => navigateToApp('search'), name: 'Start Exploring' },
+ { selector: '.cta-button.secondary', action: () => navigateToApp('upload'), name: 'Upload Paper' },
+ { selector: '.back-to-landing', action: () => navigateToLanding(), name: 'Back to Landing' },
+ { selector: '.app-nav .nav-brand', action: () => navigateToLanding(), name: 'Brand Logo' }
+ ];
+
+ buttons.forEach(({ selector, action, name }) => {
+ const button = document.querySelector(selector);
+ if (button) {
+ console.log(`✅ Setting up ${name} button`);
+ button.removeAttribute('onclick');
+ button.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log(`${name} button clicked!`);
+ action();
+ });
+ } else {
+ console.log(`❌ ${name} button not found`);
+ }
+ });
+
+ // Also setup summary page buttons if they exist
+ setupSummaryPageButtonsGlobal();
+}
+
+// Global function to setup summary page buttons
+function setupSummaryPageButtonsGlobal() {
+ console.log('Setting up summary page buttons globally...');
+
+ // Summary page buttons with fallback functions
+ const summaryButtons = [
+ { selector: '.back-btn', action: () => goBackToSearch(), name: 'Back to Search' },
+ { selector: '.summary-action-btn[title*="Copy"]', action: () => copySummary(), name: 'Copy Summary' },
+ { selector: '.summary-action-btn[title*="Regenerate"]', action: () => regenerateSummary(), name: 'Regenerate Summary' },
+ { selector: '.action-btn-header[title*="Export"]', action: () => exportSummaryChat(), name: 'Export Summary' },
+ { selector: '.action-btn-header[title*="Share"]', action: () => shareSummary(), name: 'Share Summary' }
+ ];
+
+ summaryButtons.forEach(({ selector, action, name }) => {
+ const button = document.querySelector(selector);
+ if (button) {
+ console.log(`✅ Setting up ${name} button`);
+ button.removeAttribute('onclick');
+ // Clone button to remove all existing event listeners
+ const newButton = button.cloneNode(true);
+ button.parentNode.replaceChild(newButton, button);
+
+ newButton.addEventListener('click', (e) => {
+ e.preventDefault();
+ console.log(`${name} button clicked!`);
+ action();
+ });
+ } else {
+ console.log(`❌ ${name} button not found`);
+ }
+ });
+
+ // Setup quick question buttons
+ const quickQuestionBtns = document.querySelectorAll('.quick-question-btn');
+ console.log(`Found ${quickQuestionBtns.length} quick question buttons`);
+ quickQuestionBtns.forEach((btn, index) => {
+ btn.removeAttribute('onclick');
+ // Clone button to remove existing listeners
+ const newBtn = btn.cloneNode(true);
+ btn.parentNode.replaceChild(newBtn, btn);
+
+ newBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ const question = newBtn.textContent.trim();
+ console.log(`Quick question button ${index + 1} clicked: ${question}`);
+ askQuickQuestion(question);
+ });
+ });
+}
+
+// Run critical setup immediately if DOM is already loaded
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', setupCriticalButtons);
+} else {
+ setupCriticalButtons();
+}
+
+// Initialize the application when DOM is loaded
+document.addEventListener('DOMContentLoaded', () => {
+ console.log('DOM Content Loaded - Initializing Research Radar...');
+
+ // Small delay to ensure all elements are rendered
+ setTimeout(() => {
+ window.researchRadar = new ResearchRadar();
+ console.log('🚀 Research Radar - Application initialized successfully!');
+
+ // Test if critical elements exist
+ const testElements = [
+ 'searchInput',
+ 'searchBtn',
+ 'analyzeUrlBtn',
+ 'fileInput',
+ 'searchResults'
+ ];
+
+ testElements.forEach(id => {
+ const element = document.getElementById(id);
+ console.log(`Element ${id}:`, element ? 'Found' : 'Not found');
+ });
+
+ // Test if Generate Summary buttons exist (they might be created dynamically)
+ setTimeout(() => {
+ const generateButtons = document.querySelectorAll('.generate-summary-btn');
+ console.log(`Dynamic Generate Summary buttons found: ${generateButtons.length}`);
+ }, 1000);
+
+ }, 100);
+});
+// Add some additional CSS for animations
+const additionalCSS = `
+@keyframes toastSlideOut {
+ to {
+ opacity: 0;
+ transform: translateX(100%);
+ }
+}
+`;
+
+const styleSheet = document.createElement('style');
+styleSheet.textContent = additionalCSS;
+document.head.appendChild(styleSheet);
+
+// Tab switching functionality
+function switchTab(tabName) {
+ // Strict tabs: only one panel visible
+ document.querySelectorAll('.tab-btn').forEach(btn => {
+ btn.classList.remove('active');
+ });
+ document.querySelectorAll('.tab-content').forEach(content => {
+ content.classList.remove('active');
+ });
+ document.querySelector(`[data-tab="${tabName}"]`)?.classList.add('active');
+ document.getElementById(`${tabName}-tab`)?.classList.add('active');
+ if (tabName === 'chat') {
+ setTimeout(() => {
+ const chatInput = document.getElementById('chatInputPanel');
+ if (chatInput) chatInput.focus();
+ }, 100);
+ }
+ history.replaceState(null, null, `#${tabName}`);
+ const tabDisplayName = tabName === 'summary' ? 'Summary' : 'Chat';
+ showToast(`Switched to ${tabDisplayName} tab`, 'info');
+}
+
+// Initialize tab from URL hash
+function initializeTabFromHash() {
+ const hash = window.location.hash.substring(1);
+ if (hash === 'summary' || hash === 'chat') {
+ switchTab(hash);
+ }
+}
+
+// Quick question functionality
+function askQuickQuestion(question) {
+ const chatInput = document.getElementById('chatInputPanel');
+ if (chatInput) {
+ chatInput.value = question;
+ chatInput.focus();
+ }
+}
+
+// Enhanced chat input functionality
+function initializeChatInput() {
+ const chatInput = document.getElementById('chatInputPanel');
+ const sendBtn = document.getElementById('chatSendBtnPanel');
+
+ if (chatInput && sendBtn) {
+ // Auto-resize textarea
+ chatInput.addEventListener('input', function() {
+ this.style.height = 'auto';
+ this.style.height = Math.min(this.scrollHeight, 120) + 'px';
+ });
+
+ // Handle Enter key
+ chatInput.addEventListener('keydown', function(e) {
+ if (e.key === 'Enter') {
+ if (e.ctrlKey || e.metaKey) {
+ // Ctrl+Enter or Cmd+Enter to send
+ e.preventDefault();
+ sendChatMessage();
+ } else if (!e.shiftKey) {
+ // Enter to send (unless Shift+Enter for new line)
+ e.preventDefault();
+ sendChatMessage();
+ }
+ }
+ });
+
+ // Send button click
+ sendBtn.addEventListener('click', sendChatMessage);
+ }
+}
+
+// Send chat message functionality
+function sendChatMessage() {
+ const chatInput = document.getElementById('chatInputPanel');
+ const message = chatInput.value.trim();
+
+ if (!message) {
+ if (window.researchRadar) {
+ window.researchRadar.showToast('Please enter a message', 'warning');
+ }
+ return;
+ }
+
+ // Add user message to chat
+ addMessageToChat('user', message);
+
+ // Clear input
+ chatInput.value = '';
+ chatInput.style.height = 'auto';
+
+ // Show typing indicator
+ showTypingIndicator();
+
+ // Call backend chat API
+ fetch('/chat', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ message })
+ })
+ .then(res => res.json())
+ .then(data => {
+ hideTypingIndicator();
+ if (data && data.success) {
+ addMessageToChat('assistant', data.response || '');
+ } else {
+ addMessageToChat('assistant', `Error: ${data?.error || 'Unknown error'}`);
+ }
+ })
+ .catch(err => {
+ console.error('Chat error:', err);
+ hideTypingIndicator();
+ addMessageToChat('assistant', 'Network error. Please try again.');
+ });
+}
+
+// Add message to chat
+function addMessageToChat(sender, message) {
+ const chatContainer = document.getElementById('chatMessagesPanel');
+ const messageElement = document.createElement('div');
+ messageElement.className = `chat-message ${sender}`;
+
+ const timestamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
+
+ const avatarIcon = sender === 'user' ? 'fa-user' : 'fa-robot';
+
+ messageElement.innerHTML = `
+
+
+
+
+
+
+ ${sender === 'user' ? 'You' : 'AI Assistant'} • ${timestamp}
+
+
+ `;
+
+ // Remove welcome message if it exists
+ const welcomeMessage = chatContainer.querySelector('.chat-welcome');
+ if (welcomeMessage) {
+ welcomeMessage.style.display = 'none';
+ }
+
+ chatContainer.appendChild(messageElement);
+
+ // Scroll to bottom
+ chatContainer.scrollTop = chatContainer.scrollHeight;
+}
+
+// Show typing indicator
+function showTypingIndicator() {
+ const chatContainer = document.getElementById('chatMessagesPanel');
+ // Prevent adding multiple indicators
+ if (document.getElementById('typingIndicator')) return;
+
+ const typingIndicator = document.createElement('div');
+ typingIndicator.className = 'typing-indicator chat-message assistant';
+ typingIndicator.id = 'typingIndicator';
+
+ typingIndicator.innerHTML = `
+
+
+
+
+ `;
+
+ chatContainer.appendChild(typingIndicator);
+ chatContainer.scrollTop = chatContainer.scrollHeight;
+}
+
+// Hide typing indicator
+function hideTypingIndicator() {
+ const typingIndicator = document.getElementById('typingIndicator');
+ if (typingIndicator) {
+ typingIndicator.remove();
+ }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', function() {
+ // ... existing code ...
+
+ // Initialize tab functionality
+ initializeTabFromHash();
+ initializeChatInput();
+
+ // Listen for hash changes
+ window.addEventListener('hashchange', initializeTabFromHash);
+
+ // ... existing code ...
+});