Jatin Mehra
Clear saved session on page refresh
ba76b7d
// DOM Elements
const uploadBox = document.getElementById('upload-box');
const pdfUpload = document.getElementById('pdf-upload');
const welcomeScreen = document.getElementById('welcome-screen');
const chatContainer = document.getElementById('chat-container');
const chatMessages = document.getElementById('chat-messages');
const chatInput = document.getElementById('chat-input');
const sendButton = document.getElementById('send-button');
const searchToggle = document.getElementById('search-toggle');
const modelSelect = document.getElementById('model-select');
const sessionInfo = document.getElementById('session-info');
const currentFileName = document.getElementById('current-file-name');
const clearHistoryBtn = document.getElementById('clear-history');
const removePdfBtn = document.getElementById('remove-pdf');
const newChatBtn = document.getElementById('new-chat');
const loadingOverlay = document.getElementById('loading-overlay');
const loadingText = document.getElementById('loading-text');
const getStartedBtn = document.getElementById('get-started-btn');
const contextContent = document.getElementById('context-content');
const contextSidebar = document.getElementById('context-sidebar');
const toggleContextBtn = document.getElementById('toggle-context');
const menuToggle = document.getElementById('menu-toggle');
const sidebar = document.querySelector('.sidebar');
// App state
let currentSessionId = null;
let lastContextData = null;
let isMobile = window.innerWidth <= 768;
// Event listeners
uploadBox.addEventListener('click', () => pdfUpload.click());
pdfUpload.addEventListener('change', handleFileUpload);
sendButton.addEventListener('click', sendMessage);
chatInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
chatInput.addEventListener('input', () => {
sendButton.disabled = chatInput.value.trim() === '';
});
clearHistoryBtn.addEventListener('click', clearChatHistory);
removePdfBtn.addEventListener('click', removePdf);
newChatBtn.addEventListener('click', resetApp);
getStartedBtn.addEventListener('click', () => {
uploadBox.click();
});
toggleContextBtn.addEventListener('click', toggleContextSidebar);
// Mobile menu event listeners
if (menuToggle) {
menuToggle.addEventListener('click', () => {
sidebar.classList.toggle('show');
});
}
// Handle window resize
window.addEventListener('resize', () => {
const wasMobile = isMobile;
isMobile = window.innerWidth <= 768;
// If we're transitioning between mobile/desktop
if (wasMobile !== isMobile) {
updateMobileUI();
}
});
// Initialize the app
initializeApp();
// Functions
function initializeApp() {
updateMobileUI();
// Check if the page was refreshed
const pageWasRefreshed = (
window.performance &&
window.performance.navigation &&
window.performance.navigation.type === 1
) || document.referrer === document.location.href;
// If page was refreshed, clear any saved session
if (pageWasRefreshed) {
localStorage.removeItem('pdf_insight_session');
return;
}
// Check if we have a session in localStorage
const savedSession = localStorage.getItem('pdf_insight_session');
if (savedSession) {
try {
const session = JSON.parse(savedSession);
currentSessionId = session.id;
currentFileName.textContent = session.fileName;
// Show chat interface
welcomeScreen.classList.add('hidden');
chatContainer.classList.remove('hidden');
sessionInfo.classList.remove('hidden');
// Load chat history
fetchChatHistory();
} catch (e) {
console.error('Failed to load saved session:', e);
localStorage.removeItem('pdf_insight_session');
}
}
}
// Update UI based on mobile/desktop view
function updateMobileUI() {
if (isMobile) {
if (menuToggle) menuToggle.classList.remove('hidden');
contextSidebar.classList.add('collapsed');
} else {
if (menuToggle) menuToggle.classList.add('hidden');
sidebar.classList.remove('show');
}
}
async function handleFileUpload(e) {
const file = e.target.files[0];
if (!file) return;
try {
// Show loading overlay
showLoading('Processing Document...');
const formData = new FormData();
formData.append('file', file);
formData.append('model_name', modelSelect.value);
const response = await fetch('/upload-pdf', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.status === 'success') {
currentSessionId = data.session_id;
currentFileName.textContent = file.name;
// Save session to localStorage
localStorage.setItem('pdf_insight_session', JSON.stringify({
id: currentSessionId,
fileName: file.name
}));
// Show chat interface
welcomeScreen.classList.add('hidden');
chatContainer.classList.remove('hidden');
sessionInfo.classList.remove('hidden');
// Reset chat history view
chatMessages.innerHTML = `
<div class="system-message">
<p>Upload successful! You can now ask questions about "${file.name}".</p>
</div>
`;
// Enable input
chatInput.disabled = false;
chatInput.placeholder = 'Ask a question about the document...';
// Close sidebar on mobile after uploading
if (isMobile) {
sidebar.classList.remove('show');
}
} else {
// Enhanced error display
let errorDetails = '';
if (data.detail) {
errorDetails = data.detail;
}
if (data.type) {
errorDetails = `${data.type}: ${errorDetails}`;
}
showError('Error: ' + (errorDetails || 'Failed to process document'));
// Add a more detailed error in the chat area if we're already in chat mode
if (!welcomeScreen.classList.contains('hidden')) {
const errorMessageDiv = document.createElement('div');
errorMessageDiv.className = 'system-message error';
errorMessageDiv.innerHTML = `
<p><strong>Error Processing Document</strong></p>
<p>${errorDetails}</p>
<p>Please make sure you have set up all required API keys in the .env file.</p>
`;
chatMessages.appendChild(errorMessageDiv);
}
}
} catch (error) {
console.error('Error uploading file:', error);
showError('Failed to upload document. Please try again.');
} finally {
hideLoading();
}
}
async function sendMessage() {
const query = chatInput.value.trim();
if (!query || !currentSessionId) return;
// Disable input and show typing indicator
chatInput.disabled = true;
sendButton.disabled = true;
// Add user message to chat
const userMessageElement = createMessageElement('user', query);
chatMessages.appendChild(userMessageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Clear input
chatInput.value = '';
try {
// Show loading state
const typingIndicator = createTypingIndicator();
chatMessages.appendChild(typingIndicator);
chatMessages.scrollTop = chatMessages.scrollHeight;
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
session_id: currentSessionId,
query: query,
use_search: searchToggle.checked,
model_name: modelSelect.value
})
});
const data = await response.json();
// Remove typing indicator
chatMessages.removeChild(typingIndicator);
if (data.status === 'success') {
// Add assistant message
const assistantMessageElement = createMessageElement('assistant', data.answer);
chatMessages.appendChild(assistantMessageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Apply syntax highlighting to code blocks
applyCodeHighlighting();
// Update context sidebar
updateContextSidebar(data.context_used);
lastContextData = data.context_used;
} else {
showError('Failed to get response: ' + data.detail);
}
} catch (error) {
console.error('Error sending message:', error);
showError('Failed to get response. Please try again.');
// Remove typing indicator if it exists
const indicator = document.querySelector('.typing-indicator');
if (indicator) {
chatMessages.removeChild(indicator);
}
} finally {
// Re-enable input
chatInput.disabled = false;
chatInput.focus();
}
}
async function fetchChatHistory() {
if (!currentSessionId) return;
try {
showLoading('Loading chat history...');
const response = await fetch('/chat-history', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
session_id: currentSessionId
})
});
const data = await response.json();
if (data.status === 'success' && data.history.length > 0) {
// Clear chat messages and add system message
chatMessages.innerHTML = `
<div class="system-message">
<p>Continuing your conversation about "${currentFileName.textContent}".</p>
</div>
`;
// Add all messages from history
data.history.forEach(item => {
const userMessage = createMessageElement('user', item.user);
const assistantMessage = createMessageElement('assistant', item.assistant);
chatMessages.appendChild(userMessage);
chatMessages.appendChild(assistantMessage);
});
// Apply syntax highlighting to code blocks
applyCodeHighlighting();
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
}
} catch (error) {
console.error('Error fetching chat history:', error);
showError('Failed to load chat history.');
} finally {
hideLoading();
}
}
async function clearChatHistory() {
if (!currentSessionId) return;
try {
showLoading('Clearing chat history...');
const response = await fetch('/clear-history', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
session_id: currentSessionId
})
});
const data = await response.json();
if (data.status === 'success') {
// Reset chat messages
chatMessages.innerHTML = `
<div class="system-message">
<p>Chat history cleared. You can continue asking questions about "${currentFileName.textContent}".</p>
</div>
`;
// Reset context
contextContent.innerHTML = '<p class="no-context">No context available yet. Ask a question first.</p>';
lastContextData = null;
} else {
showError('Failed to clear chat history: ' + data.detail);
}
} catch (error) {
console.error('Error clearing chat history:', error);
showError('Failed to clear chat history.');
} finally {
hideLoading();
}
}
async function removePdf() {
if (!currentSessionId) return;
try {
showLoading('Removing PDF from the system...');
const response = await fetch('/remove-pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
session_id: currentSessionId
})
});
const data = await response.json();
if (data.status === 'success') {
// Reset the app
resetApp();
showError('PDF file has been removed from the system.');
} else {
showError('Failed to remove PDF: ' + data.detail);
}
} catch (error) {
console.error('Error removing PDF:', error);
showError('Failed to remove PDF. Please try again.');
} finally {
hideLoading();
}
}
function resetApp() {
// Clear current session
currentSessionId = null;
lastContextData = null;
localStorage.removeItem('pdf_insight_session');
// Reset UI
welcomeScreen.classList.remove('hidden');
chatContainer.classList.add('hidden');
sessionInfo.classList.add('hidden');
// Reset file input
pdfUpload.value = '';
// Reset chat messages
chatMessages.innerHTML = `
<div class="system-message">
<p>Upload successful! You can now ask questions about the document.</p>
</div>
`;
// Reset context sidebar
contextContent.innerHTML = '<p class="no-context">No context available yet. Ask a question first.</p>';
}
function createMessageElement(type, content) {
const div = document.createElement('div');
div.className = `message ${type}-message`;
// For user messages, just escape HTML
if (type === 'user') {
div.innerHTML = `
<div class="message-content">${escapeHTML(content)}</div>
<div class="message-timestamp">${formatTimestamp(new Date())}</div>
`;
}
// For assistant messages, render with Markdown
else {
// Configure marked.js options
marked.setOptions({
breaks: true, // Add <br> on single line breaks
gfm: true, // GitHub Flavored Markdown
sanitize: false // Allow HTML in the input
});
// Process the content with marked
const renderedContent = marked.parse(content);
div.innerHTML = `
<div class="message-content">${renderedContent}</div>
<div class="message-timestamp">${formatTimestamp(new Date())}</div>
`;
}
return div;
}
function createTypingIndicator() {
const div = document.createElement('div');
div.className = 'message assistant-message typing-indicator';
div.innerHTML = `
<div class="typing-animation">
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
`;
return div;
}
function updateContextSidebar(contextData) {
if (!contextData || contextData.length === 0) {
contextContent.innerHTML = '<p class="no-context">No context available for this response.</p>';
return;
}
contextContent.innerHTML = '';
contextData.forEach((item, index) => {
const contextItem = document.createElement('div');
contextItem.className = 'context-item';
const score = Math.round((1 - item.score) * 100); // Convert distance to similarity score
contextItem.innerHTML = `
<span class="context-score">Relevance: ${score}%</span>
<div class="context-text">${truncateText(item.text, 300)}</div>
`;
contextContent.appendChild(contextItem);
});
}
function toggleContextSidebar() {
contextSidebar.classList.toggle('collapsed');
// Update icon
const icon = toggleContextBtn.querySelector('i');
if (contextSidebar.classList.contains('collapsed')) {
icon.className = 'fas fa-angle-left';
} else {
icon.className = 'fas fa-angle-right';
}
}
function applyCodeHighlighting() {
// Find all code blocks in the assistant messages
document.querySelectorAll('.assistant-message pre code').forEach(block => {
hljs.highlightElement(block);
});
}
function showLoading(message) {
loadingText.textContent = message || 'Loading...';
loadingOverlay.classList.remove('hidden');
}
function hideLoading() {
loadingOverlay.classList.add('hidden');
}
function showError(message) {
// Add error message to chat
const errorDiv = document.createElement('div');
errorDiv.className = 'system-message error';
errorDiv.innerHTML = `<p>${message}</p>`;
chatMessages.appendChild(errorDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Also show an alert for critical errors
if (message.includes('API_KEY') || message.includes('environment variables')) {
alert('Configuration Error: ' + message);
}
// Remove after 10 seconds
setTimeout(() => {
if (errorDiv.parentNode === chatMessages) {
chatMessages.removeChild(errorDiv);
}
}, 10000);
}
// Helper functions
function formatTimestamp(date) {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
function truncateText(text, maxLength) {
if (text.length <= maxLength) return text;
return text.substring(0, maxLength) + '...';
}
function escapeHTML(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}