document.addEventListener('DOMContentLoaded', () => {
const API_URL = ''; // Use relative path for API calls
// --- Application Elements ---
const nasaSearchInput = document.getElementById('nasaSearchInput');
const nasaSearchBtn = document.getElementById('nasaSearchBtn');
const searchResultsList = document.getElementById('search-results-list');
const trainChatbotBtn = document.getElementById('trainChatbotBtn');
const chatHistory = document.getElementById('chat-history');
const chatInput = document.getElementById('chat-input');
const chatSendBtn = document.getElementById('chat-send');
// --- Application State ---
let currentSessionId = null;
// --- Helper Functions ---
function addChatMessage(sender, message, sources = []) {
const messageElement = document.createElement('div');
messageElement.classList.add('chat-message', sender.toLowerCase());
messageElement.style.marginBottom = '12px';
// Use innerText for safety, but replace newlines with
for rendering
const formattedMessage = message.replace(/\n/g, '
');
let sourcesHTML = '';
if (sources.length > 0) {
sourcesHTML = '
Searching...
'; trainChatbotBtn.style.display = 'none'; nasaSearchBtn.disabled = true; nasaSearchBtn.textContent = 'Searching...'; try { const response = await fetch(`${API_URL}/api/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ keyword }), }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const results = await response.json(); displaySearchResults(results); } catch (error) { console.error('Search failed:', error); searchResultsList.innerHTML = `Search failed. Please check the console or try again.
`; } finally { nasaSearchBtn.disabled = false; nasaSearchBtn.textContent = 'Search'; } }); // 2. Display Search Results function displaySearchResults(results) { searchResultsList.innerHTML = ''; if (results.length === 0) { searchResultsList.innerHTML = 'No papers with downloadable PDFs found for this keyword.
'; return; } results.forEach(paper => { const paperElement = document.createElement('div'); paperElement.classList.add('paper-item'); paperElement.style.marginBottom = '15px'; paperElement.innerHTML = ` `; searchResultsList.appendChild(paperElement); }); trainChatbotBtn.style.display = 'block'; } // 3a. Poll for training status function pollTrainingStatus(jobId) { const interval = setInterval(async () => { try { const response = await fetch(`${API_URL}/api/train/status/${jobId}`); // Gracefully handle server restarts where the job is lost if (response.status === 404) { clearInterval(interval); throw new Error("Training process was interrupted on the server. Please try again."); } const result = await response.json(); if (result.status === 'processing' || result.status === 'starting') { addChatMessage('System', result.message); } else if (result.status === 'complete') { clearInterval(interval); currentSessionId = result.sessionId; addChatMessage('Bot', result.message + " You can now ask me questions about them."); trainChatbotBtn.disabled = false; trainChatbotBtn.textContent = 'Train Chatbot on Selected Papers'; } else if (result.status === 'error') { clearInterval(interval); throw new Error(result.message || 'Training job failed on the server.'); } } catch (error) { clearInterval(interval); console.error('Polling failed:', error); addChatMessage('System', `Error during training: ${error.message}`); trainChatbotBtn.disabled = false; trainChatbotBtn.textContent = 'Train Chatbot on Selected Papers'; } }, 3000); // Check every 3 seconds } // 3b. Display selected papers and hide search results function showSelectedPapers(papers) { const searchPanel = document.getElementById('search-panel'); const selectedPapersPanel = document.getElementById('selected-papers-panel'); const selectedPapersList = document.getElementById('selected-papers-list'); selectedPapersList.innerHTML = ''; // Clear previous list papers.forEach(paper => { const paperElement = document.createElement('div'); paperElement.innerHTML = ` ${paper.title} `; selectedPapersList.appendChild(paperElement); }); searchPanel.style.display = 'none'; selectedPapersPanel.style.display = 'block'; } // 3. Train Chatbot trainChatbotBtn.addEventListener('click', async () => { const selectedCheckboxes = document.querySelectorAll('.paper-checkbox:checked'); const papers = Array.from(selectedCheckboxes).map(cb => ({ id: cb.dataset.paperId, url: cb.dataset.pdfUrl, title: cb.closest('.paper-item').querySelector('strong').textContent, })); if (papers.length === 0) { alert('Please select at least one paper to train the chatbot.'); return; } addChatMessage('System', `Training chatbot on ${papers.length} paper(s)... This may take a moment.`); trainChatbotBtn.disabled = true; trainChatbotBtn.textContent = 'Training...'; // Show the selected papers list and hide the search results showSelectedPapers(papers); // Activate the three-column layout for the knowledge map document.getElementById('main-app-container').classList.add('knowledge-map-active'); try { const response = await fetch(`${API_URL}/api/train`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ papers }), }); const result = await response.json(); if (response.status === 202) { // 202 Accepted addChatMessage('System', result.message); pollTrainingStatus(result.jobId); // Start polling for status } else { throw new Error(result.error || 'Failed to start training job.'); } } catch (error) { console.error('Training failed:', error); addChatMessage('System', `Error during training: ${error.message}`); } }); // 4. Handle Chat Messages async function handleSendMessage() { const query = chatInput.value.trim(); if (!query) return; if (!currentSessionId) { addChatMessage('System', 'Error: No active training session. Please train the chatbot first.'); return; } addChatMessage('You', query); chatInput.value = ''; chatSendBtn.disabled = true; try { const response = await fetch(`${API_URL}/api/ask`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, sessionId: currentSessionId }), }); const result = await response.json(); if (!response.ok) throw new Error(result.error || 'Failed to get an answer.'); addChatMessage('Bot', result.answer, result.sources); } catch (error) { console.error('Ask failed:', error); addChatMessage('System', `Error: ${error.message}`); } finally { chatSendBtn.disabled = false; } } chatSendBtn.addEventListener('click', handleSendMessage); chatInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { handleSendMessage(); } }); // --- Original Theme JavaScript --- // (The existing code for particles, menu, text rotation, etc., goes here) // Create floating particles function createParticles() { const particlesContainer = document.getElementById('particles'); const particleCount = 30; for (let i = 0; i < particleCount; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.left = Math.random() * 100 + '%'; particle.style.animationDelay = Math.random() * 15 + 's'; particle.style.animationDuration = (Math.random() * 10 + 15) + 's'; // Randomly assign orange or blue color if (Math.random() > 0.5) { particle.style.setProperty('--particle-color', '#00B2FF'); const before = particle.style.getPropertyValue('--particle-color'); particle.style.background = '#00B2FF'; } particlesContainer.appendChild(particle); } } // Mobile menu toggle const menuToggle = document.getElementById('menuToggle'); const navLinks = document.getElementById('navLinks'); menuToggle.addEventListener('click', () => { menuToggle.classList.toggle('active'); navLinks.classList.toggle('active'); }); // Close mobile menu when clicking a link document.querySelectorAll('.nav-links a').forEach(link => { link.addEventListener('click', () => { menuToggle.classList.remove('active'); navLinks.classList.remove('active'); }); }); // Active navigation highlighting const sections = document.querySelectorAll('section'); const navItems = document.querySelectorAll('.nav-link'); function updateActiveNav() { const scrollPosition = window.pageYOffset + 100; sections.forEach((section, index) => { const sectionTop = section.offsetTop; const sectionHeight = section.offsetHeight; if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) { navItems.forEach(item => item.classList.remove('active')); const currentNav = document.querySelector(`.nav-link[href="#${section.id}"]`); if (currentNav) currentNav.classList.add('active'); } }); } // Navbar scroll effect window.addEventListener('scroll', function() { const navbar = document.getElementById('navbar'); if (window.scrollY > 50) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } updateActiveNav(); }); // Initial active nav update updateActiveNav(); // Smooth scrolling for navigation links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Feature tabs functionality const tabs = document.querySelectorAll('.tab-item'); const panels = document.querySelectorAll('.content-panel'); tabs.forEach(tab => { tab.addEventListener('click', () => { const tabId = tab.getAttribute('data-tab'); // Remove active class from all tabs and panels tabs.forEach(t => t.classList.remove('active')); panels.forEach(p => p.classList.remove('active')); // Add active class to clicked tab and corresponding panel tab.classList.add('active'); document.getElementById(tabId).classList.add('active'); }); }); // Form submission document.getElementById('contactForm').addEventListener('submit', function(e) { e.preventDefault(); // Add your form submission logic here alert('Message sent! We\'ll get back to you soon.'); this.reset(); }); // Initialize particles createParticles(); // Text rotation with character animation const textSets = document.querySelectorAll('.text-set'); let currentIndex = 0; let isAnimating = false; function wrapTextInSpans(element) { const text = element.textContent; element.innerHTML = text.split('').map((char, i) => `${char === ' ' ? ' ' : char}` ).join(''); } function animateTextIn(textSet) { const glitchText = textSet.querySelector('.glitch-text'); const subtitle = textSet.querySelector('.subtitle'); // Wrap text in spans for animation wrapTextInSpans(glitchText); // Update data attribute for glitch effect glitchText.setAttribute('data-text', glitchText.textContent); // Show subtitle after main text setTimeout(() => { subtitle.classList.add('visible'); }, 800); } function animateTextOut(textSet) { const chars = textSet.querySelectorAll('.char'); const subtitle = textSet.querySelector('.subtitle'); // Animate characters out chars.forEach((char, i) => { char.style.animationDelay = `${i * 0.02}s`; char.classList.add('out'); }); // Hide subtitle subtitle.classList.remove('visible'); } function rotateText() { if (isAnimating) return; isAnimating = true; const currentSet = textSets[currentIndex]; const nextIndex = (currentIndex + 1) % textSets.length; const nextSet = textSets[nextIndex]; // Animate out current text animateTextOut(currentSet); // After out animation, switch sets setTimeout(() => { currentSet.classList.remove('active'); nextSet.classList.add('active'); animateTextIn(nextSet); currentIndex = nextIndex; isAnimating = false; }, 600); } // Initialize first text set textSets[0].classList.add('active'); animateTextIn(textSets[0]); // Start rotation after initial display setTimeout(() => { setInterval(rotateText, 5000); // Change every 5 seconds }, 4000); // Add random glitch effect setInterval(() => { const glitchTexts = document.querySelectorAll('.glitch-text'); glitchTexts.forEach(text => { if (Math.random() > 0.95) { text.style.animation = 'none'; setTimeout(() => { text.style.animation = ''; }, 200); } }); }, 3000); });