|
document.addEventListener('DOMContentLoaded', function() { |
|
const chatContainer = document.getElementById('chat-container'); |
|
const followUpContainer = document.getElementById('follow-up-container'); |
|
const userInput = document.getElementById('user-input'); |
|
const sendButton = document.getElementById('send-button'); |
|
const statusIndicator = document.getElementById('status-indicator'); |
|
const leftPanel = document.querySelector('.left-panel'); |
|
const rightPanel = document.querySelector('.right-panel'); |
|
const resizer = document.getElementById('resizer'); |
|
const splitScreen = document.querySelector('.split-screen'); |
|
|
|
let isResizing = false; |
|
|
|
resizer.addEventListener('mousedown', initResize); |
|
document.addEventListener('mousemove', resize); |
|
document.addEventListener('mouseup', stopResize); |
|
|
|
function initResize(e) { |
|
isResizing = true; |
|
document.body.style.cursor = 'col-resize'; |
|
} |
|
|
|
function resize(e) { |
|
if (!isResizing) return; |
|
const containerWidth = splitScreen.offsetWidth; |
|
const newLeftWidth = e.clientX - leftPanel.offsetLeft; |
|
const newRightWidth = containerWidth - newLeftWidth - resizer.offsetWidth; |
|
|
|
if (newLeftWidth > 300 && newRightWidth > 300) { |
|
const leftPercentage = (newLeftWidth / containerWidth) * 100; |
|
const rightPercentage = (newRightWidth / containerWidth) * 100; |
|
document.documentElement.style.setProperty('--chat-width', `${leftPercentage}%`); |
|
document.documentElement.style.setProperty('--pdf-width', `${rightPercentage}%`); |
|
} |
|
} |
|
|
|
function stopResize() { |
|
isResizing = false; |
|
document.body.style.cursor = 'default'; |
|
} |
|
|
|
function updateStatus() { |
|
fetch('/status') |
|
.then(response => response.json()) |
|
.then(data => { |
|
const statusBulb = document.getElementById('status-bulb'); |
|
statusIndicator.title = data.status; |
|
statusBulb.style.backgroundColor = data.status.includes("Connected") ? "green" : "red"; |
|
}); |
|
} |
|
|
|
function sendMessage() { |
|
const question = userInput.value.trim(); |
|
if (question) { |
|
addMessage('user', question); |
|
userInput.value = ''; |
|
clearFollowUpQuestions(); |
|
showSearchingIndicator(); |
|
|
|
fetch('/ask', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify({ question: question }), |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
removeSearchingIndicator(); |
|
addMessage('bot', data.response); |
|
addSources(data.sources); |
|
if (data.follow_up_questions) { |
|
setTimeout(() => { |
|
addFollowUpQuestions(data.follow_up_questions); |
|
}, 1000); |
|
} |
|
}) |
|
.catch(error => { |
|
console.error('Error:', error); |
|
removeSearchingIndicator(); |
|
addMessage('bot', 'Sorry, there was an error processing your request.'); |
|
}); |
|
} |
|
} |
|
|
|
function addMessage(sender, message) { |
|
const messageElement = document.createElement('div'); |
|
messageElement.className = `${sender}-message message`; |
|
messageElement.textContent = message; |
|
chatContainer.appendChild(messageElement); |
|
scrollToBottom(); |
|
} |
|
|
|
function showSearchingIndicator() { |
|
const searchingElement = document.createElement('div'); |
|
searchingElement.className = 'bot-message message searching'; |
|
searchingElement.textContent = 'Searching'; |
|
searchingElement.id = 'searching-indicator'; |
|
chatContainer.appendChild(searchingElement); |
|
scrollToBottom(); |
|
} |
|
|
|
function removeSearchingIndicator() { |
|
const searchingElement = document.getElementById('searching-indicator'); |
|
if (searchingElement) { |
|
searchingElement.remove(); |
|
} |
|
} |
|
|
|
function addSources(sources) { |
|
console.log("Raw sources:", sources); |
|
if (sources && sources.trim() !== "") { |
|
const sourcesElement = document.createElement('div'); |
|
sourcesElement.className = 'sources'; |
|
sourcesElement.innerHTML = '<strong>Sources:</strong><br>'; |
|
|
|
const sourcesList = sources.split(/(?=\d+\.\s+(?:Text|Image Description) from)/).filter(Boolean); |
|
console.log("Split sources:", sourcesList); |
|
|
|
sourcesList.forEach((source) => { |
|
const sourceInfo = parseSourceInfo(source.trim()); |
|
if (sourceInfo) { |
|
const sourceLink = document.createElement('a'); |
|
sourceLink.href = '#'; |
|
sourceLink.textContent = `${sourceInfo.index}. ${sourceInfo.type} from ${sourceInfo.fileName} (Page ${sourceInfo.page}${sourceInfo.additional ? ', ' + sourceInfo.additional : ''})`; |
|
sourceLink.onclick = function(e) { |
|
e.preventDefault(); |
|
openSource(sourceInfo); |
|
}; |
|
sourcesElement.appendChild(sourceLink); |
|
sourcesElement.appendChild(document.createElement('br')); |
|
} |
|
}); |
|
|
|
chatContainer.appendChild(sourcesElement); |
|
scrollToBottom(); |
|
} else { |
|
console.log("No sources provided or empty sources string"); |
|
} |
|
} |
|
|
|
function addFollowUpQuestions(questions) { |
|
clearFollowUpQuestions(); |
|
if (followUpContainer && questions && questions.length > 0) { |
|
questions.forEach((question, index) => { |
|
const button = document.createElement('button'); |
|
button.textContent = `${index + 1}. ${question}`; |
|
button.className = 'follow-up-button'; |
|
button.addEventListener('click', () => { |
|
userInput.value = question; |
|
sendMessage(); |
|
}); |
|
followUpContainer.appendChild(button); |
|
}); |
|
} |
|
} |
|
|
|
function clearFollowUpQuestions() { |
|
followUpContainer.innerHTML = ''; |
|
} |
|
|
|
function scrollToBottom() { |
|
chatContainer.scrollTop = chatContainer.scrollHeight; |
|
} |
|
|
|
function parseSourceInfo(source) { |
|
console.log("parseSourceInfo input:", source); |
|
|
|
const regex = /(\d+)\.\s+(Text|Image Description) from (.*?\.pdf)(?:\s+\(Page (\d+)(?:,\s*(.+))?\))?/; |
|
const match = source.match(regex); |
|
|
|
if (match) { |
|
const result = { |
|
index: match[1], |
|
type: match[2], |
|
fileName: match[3], |
|
page: match[4] || '1', |
|
additional: match[5] || '' |
|
}; |
|
console.log("parseSourceInfo output:", result); |
|
return result; |
|
} else { |
|
console.log("parseSourceInfo failed to match regex"); |
|
return null; |
|
} |
|
} |
|
|
|
function openSource(sourceInfo) { |
|
if (sourceInfo) { |
|
const viewerContainer = document.getElementById('viewer-container'); |
|
if (!viewerContainer) { |
|
return; |
|
} |
|
|
|
viewerContainer.innerHTML = ''; |
|
|
|
const url = `/data/${encodeURIComponent(sourceInfo.fileName)}#page=${sourceInfo.page}`; |
|
|
|
const pdfViewer = document.createElement('iframe'); |
|
pdfViewer.id = 'pdf-viewer'; |
|
pdfViewer.src = url; |
|
|
|
const closeButton = document.createElement('button'); |
|
closeButton.id = 'close-pdf'; |
|
closeButton.textContent = 'Close'; |
|
closeButton.onclick = closePDFViewer; |
|
|
|
viewerContainer.appendChild(closeButton); |
|
viewerContainer.appendChild(pdfViewer); |
|
|
|
openPDFViewer(); |
|
} |
|
} |
|
|
|
function openPDFViewer() { |
|
document.documentElement.style.setProperty('--chat-width', '50%'); |
|
document.documentElement.style.setProperty('--pdf-width', '50%'); |
|
rightPanel.style.display = 'flex'; |
|
resizer.style.display = 'block'; |
|
} |
|
|
|
function closePDFViewer() { |
|
document.documentElement.style.setProperty('--chat-width', '100%'); |
|
document.documentElement.style.setProperty('--pdf-width', '0%'); |
|
setTimeout(() => { |
|
rightPanel.style.display = 'none'; |
|
resizer.style.display = 'none'; |
|
}, 300); |
|
} |
|
|
|
function adjustPanelSizes() { |
|
const totalWidth = splitScreen.offsetWidth; |
|
if (rightPanel.style.display !== 'none') { |
|
document.documentElement.style.setProperty('--chat-width', '50%'); |
|
document.documentElement.style.setProperty('--pdf-width', '50%'); |
|
} else { |
|
document.documentElement.style.setProperty('--chat-width', '100%'); |
|
document.documentElement.style.setProperty('--pdf-width', '0%'); |
|
} |
|
} |
|
|
|
sendButton.addEventListener('click', sendMessage); |
|
userInput.addEventListener('keypress', function(e) { |
|
if (e.key === 'Enter' && !e.shiftKey) { |
|
e.preventDefault(); |
|
sendMessage(); |
|
} |
|
}); |
|
|
|
window.addEventListener('resize', adjustPanelSizes); |
|
|
|
updateStatus(); |
|
setInterval(updateStatus, 5000); |
|
|
|
|
|
rightPanel.style.display = 'none'; |
|
resizer.style.display = 'none'; |
|
document.documentElement.style.setProperty('--chat-width', '100%'); |
|
document.documentElement.style.setProperty('--pdf-width', '0%'); |
|
|
|
function checkConnection() { |
|
fetch('/check_connection') |
|
.then(response => response.json()) |
|
.then(data => { |
|
updateConnectionStatus(data.status, data.color); |
|
}) |
|
.catch(error => console.error('Error:', error)); |
|
} |
|
|
|
|
|
setInterval(checkConnection, 30000); |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', checkConnection); |
|
|
|
function updateConnectionStatus() { |
|
fetch('/status') |
|
.then(response => response.json()) |
|
.then(data => { |
|
const statusElement = document.getElementById('connection-status'); |
|
const userInput = document.getElementById('user-input'); |
|
const sendButton = document.getElementById('send-button'); |
|
|
|
if (statusElement) { |
|
statusElement.textContent = data.status; |
|
statusElement.style.color = data.color; |
|
} else { |
|
console.warn('Status element not found'); |
|
} |
|
|
|
if (userInput && sendButton) { |
|
if (data.status !== "Connected") { |
|
userInput.disabled = true; |
|
sendButton.disabled = true; |
|
userInput.placeholder = "Chat is disabled. Weaviate is not connected."; |
|
} else { |
|
userInput.disabled = false; |
|
sendButton.disabled = false; |
|
userInput.placeholder = "Type your message here..."; |
|
} |
|
} else { |
|
console.warn('Chat input or send button not found'); |
|
} |
|
}) |
|
.catch(error => { |
|
console.error('Error updating connection status:', error); |
|
}); |
|
} |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
updateConnectionStatus(); |
|
setInterval(updateConnectionStatus, 5000); |
|
}); |
|
|
|
let chatHistory = []; |
|
|
|
function sendMessage() { |
|
const userInput = document.getElementById('user-input'); |
|
const message = userInput.value.trim(); |
|
if (message) { |
|
addMessageToChat('You', message); |
|
userInput.value = ''; |
|
fetchResponse(message); |
|
} |
|
} |
|
|
|
function addMessageToChat(sender, message) { |
|
const chatMessages = document.getElementById('chat-messages'); |
|
const messageElement = document.createElement('div'); |
|
messageElement.innerHTML = `<strong>${sender}:</strong> ${message}`; |
|
chatMessages.appendChild(messageElement); |
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
} |
|
|
|
function fetchResponse(message) { |
|
fetch('/ask', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ question: message }), |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
addMessageToChat('AI', data.response); |
|
updateHistory(message, data.response, data.sources); |
|
fetchAndDisplayHistory(); |
|
}) |
|
.catch(error => { |
|
console.error('Error:', error); |
|
addMessageToChat('AI', 'Sorry, an error occurred while processing your request.'); |
|
}); |
|
} |
|
|
|
function updateHistory(question, answer, sources) { |
|
chatHistory.push({ question, answer, sources }); |
|
if (chatHistory.length > 10) { |
|
chatHistory.shift(); |
|
} |
|
} |
|
|
|
function fetchAndDisplayHistory() { |
|
const historyList = document.getElementById('history-list'); |
|
historyList.innerHTML = ''; |
|
chatHistory.forEach((item, index) => { |
|
const historyItem = document.createElement('div'); |
|
historyItem.className = 'history-item'; |
|
historyItem.innerHTML = ` |
|
<h3>Question ${index + 1}</h3> |
|
<p><strong>Q:</strong> ${item.question}</p> |
|
<p><strong>A:</strong> ${item.answer}</p> |
|
<p><strong>Sources:</strong> ${item.sources || 'N/A'}</p> |
|
`; |
|
historyList.appendChild(historyItem); |
|
}); |
|
} |
|
|
|
|
|
fetchAndDisplayHistory(); |
|
}); |