Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Patentability</title> | |
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> | |
<script> | |
// Array to store problem history versions | |
const problemHistory = []; | |
let currentHistoryIndex = -1; | |
// Store all refined problem suggestions | |
let storedRefinedProblems = {}; | |
function generateQueries(event) { | |
event.preventDefault(); | |
const userInput = document.getElementById('userInput').value.trim(); | |
if (!userInput) { | |
alert('Please enter a technical problem description'); | |
return; | |
} | |
// Show loading indicator | |
document.getElementById('loadingIndicator').style.display = 'block'; | |
// Send message to Flask backend | |
fetch('/generate-key-issues', { | |
method: 'POST', | |
body: JSON.stringify({ 'query': userInput }), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
document.getElementById('loadingIndicator').style.display = 'none'; | |
// Check if we have key issues in the response | |
if (data.key_issues && data.key_issues.length > 0) { | |
// Display the key issues | |
displayKeyIssues(data.key_issues); | |
} else if (data.error) { | |
alert('Error: ' + data.error); | |
} else { | |
alert('No key issues found. Please try a different query.'); | |
} | |
}) | |
.catch(error => { | |
document.getElementById('loadingIndicator').style.display = 'none'; | |
console.error('Error:', error); | |
alert('There was an error communicating with the server. Please try again.'); | |
}); | |
} | |
function displayKeyIssues(keyIssues) { | |
// Get or create the key issues container | |
let keyIssuesContainer = document.getElementById('keyIssuesContainer'); | |
if (!keyIssuesContainer) { | |
keyIssuesContainer = document.createElement('div'); | |
keyIssuesContainer.id = 'keyIssuesContainer'; | |
keyIssuesContainer.className = 'key-issues-container'; | |
document.getElementById('resultsContainer').appendChild(keyIssuesContainer); | |
} | |
// Clear previous content | |
keyIssuesContainer.innerHTML = ''; | |
// Create header | |
const header = document.createElement('h3'); | |
header.textContent = 'Key Issues'; | |
keyIssuesContainer.appendChild(header); | |
// Create issues list | |
const issuesList = document.createElement('div'); | |
issuesList.className = 'key-issues-list'; | |
// Add each key issue | |
keyIssues.forEach(issue => { | |
const issueCard = document.createElement('div'); | |
issueCard.className = 'key-issue-card'; | |
issueCard.dataset.id = issue.id; | |
const issueTitle = document.createElement('div'); | |
issueTitle.className = 'key-issue-title'; | |
issueTitle.textContent = issue.title; | |
const issueDescription = document.createElement('div'); | |
issueDescription.className = 'key-issue-description'; | |
issueDescription.textContent = issue.description; | |
const challengesTitle = document.createElement('div'); | |
challengesTitle.style.fontWeight = 'bold'; | |
challengesTitle.style.marginTop = '10px'; | |
challengesTitle.textContent = 'Challenges:'; | |
const challengesList = document.createElement('div'); | |
challengesList.className = 'key-issue-challenges'; | |
issue.challenges.forEach(challenge => { | |
const challengeTag = document.createElement('div'); | |
challengeTag.className = 'challenge-tag'; | |
challengeTag.textContent = challenge; | |
// Add click handler to toggle selection | |
challengeTag.addEventListener('click', function(e) { | |
e.stopPropagation(); // Prevent triggering parent card click | |
this.classList.toggle('selected'); | |
// Update selected challenges data | |
const selectedChallenges = JSON.parse(issueCard.dataset.selectedChallenges || '[]'); | |
const challengeText = this.textContent; | |
if (this.classList.contains('selected')) { | |
// Add challenge to selected list if not already there | |
if (!selectedChallenges.includes(challengeText)) { | |
selectedChallenges.push(challengeText); | |
} | |
} else { | |
// Remove challenge from selected list | |
const index = selectedChallenges.indexOf(challengeText); | |
if (index !== -1) { | |
selectedChallenges.splice(index, 1); | |
} | |
} | |
// Update dataset | |
issueCard.dataset.selectedChallenges = JSON.stringify(selectedChallenges); | |
// Update card selected state based on whether any challenges are selected | |
if (selectedChallenges.length > 0) { | |
issueCard.classList.add('selected'); | |
} else { | |
issueCard.classList.remove('selected'); | |
} | |
}); | |
challengesList.appendChild(challengeTag); | |
}); | |
const impactTitle = document.createElement('div'); | |
impactTitle.style.fontWeight = 'bold'; | |
impactTitle.style.marginTop = '10px'; | |
impactTitle.textContent = 'Potential Impact:'; | |
const issueImpact = document.createElement('div'); | |
issueImpact.className = 'key-issue-impact'; | |
issueImpact.textContent = issue.potential_impact; | |
// Add click handler to toggle selection | |
issueCard.addEventListener('click', function() { | |
this.classList.toggle('selected'); | |
}); | |
// Append all elements | |
issueCard.appendChild(issueTitle); | |
issueCard.appendChild(issueDescription); | |
issueCard.appendChild(challengesTitle); | |
issueCard.appendChild(challengesList); | |
issueCard.appendChild(impactTitle); | |
issueCard.appendChild(issueImpact); | |
issuesList.appendChild(issueCard); | |
}); | |
keyIssuesContainer.appendChild(issuesList); | |
// Show the results container | |
document.getElementById('resultsContainer').style.display = 'block'; | |
} | |
// Add a new query field with the given text (or empty if not provided) | |
function addQueryField(queryText = '', container = null) { | |
const queriesContainer = container || document.getElementById('queriesContainer'); | |
// Count existing query items in this container for indexing | |
const queryItems = container ? | |
container.querySelectorAll('.query-item') : | |
document.getElementById('queriesContainer').querySelectorAll('.query-item'); | |
const queryIndex = queryItems.length + 1; | |
// Create main container for this query item | |
const queryItem = document.createElement('div'); | |
queryItem.className = 'query-item'; | |
// Create container for query field and buttons | |
const queryContainer = document.createElement('div'); | |
queryContainer.className = 'query-container'; | |
// Input field | |
const queryField = document.createElement('input'); | |
queryField.type = 'text'; | |
queryField.className = 'query-field'; | |
queryField.value = queryText; | |
queryField.placeholder = `Search Query ${queryIndex}`; | |
// Delete button | |
const deleteButton = document.createElement('button'); | |
deleteButton.type = 'button'; | |
deleteButton.className = 'action-button delete-button'; | |
deleteButton.textContent = 'Delete'; | |
deleteButton.onclick = function() { | |
queryItem.parentNode.removeChild(queryItem); | |
// Update the placeholder numbers for remaining queries within this container | |
const parent = container || document.getElementById('queriesContainer'); | |
const items = parent.querySelectorAll('.query-item'); | |
items.forEach((item, idx) => { | |
const field = item.querySelector('.query-field'); | |
if (field) field.placeholder = `Search Query ${idx + 1}`; | |
}); | |
}; | |
// Search button | |
const searchButton = document.createElement('button'); | |
searchButton.type = 'button'; | |
searchButton.className = 'action-button search-button'; | |
searchButton.textContent = 'Search'; | |
searchButton.onclick = function() { | |
performSearch(queryField.value, queryItem); | |
}; | |
// Add elements to container | |
queryContainer.appendChild(queryField); | |
queryContainer.appendChild(searchButton); | |
queryContainer.appendChild(deleteButton); | |
// Create loading indicator for this search | |
const searchLoading = document.createElement('div'); | |
searchLoading.className = 'search-loading'; | |
searchLoading.textContent = 'Searching... Please wait.'; | |
// Create table for results | |
const resultsTable = document.createElement('table'); | |
resultsTable.className = 'results-table'; | |
// Add table header | |
const tableHeader = document.createElement('thead'); | |
const headerRow = document.createElement('tr'); | |
const typeHeader = document.createElement('th'); | |
typeHeader.textContent = 'Type'; | |
const titleHeader = document.createElement('th'); | |
titleHeader.textContent = 'Title'; | |
const bodyHeader = document.createElement('th'); | |
bodyHeader.textContent = 'Description'; | |
const urlHeader = document.createElement('th'); | |
urlHeader.textContent = 'URL'; | |
const scoreHeader = document.createElement('th'); | |
scoreHeader.textContent = 'Score'; | |
const justificationHeader = document.createElement('th'); | |
justificationHeader.textContent = 'Justification'; | |
headerRow.appendChild(typeHeader); | |
headerRow.appendChild(titleHeader); | |
headerRow.appendChild(bodyHeader); | |
headerRow.appendChild(urlHeader); | |
headerRow.appendChild(scoreHeader); | |
headerRow.appendChild(justificationHeader); | |
tableHeader.appendChild(headerRow); | |
// Add table body | |
const tableBody = document.createElement('tbody'); | |
resultsTable.appendChild(tableHeader); | |
resultsTable.appendChild(tableBody); | |
// Add all elements to the query item | |
queryItem.appendChild(queryContainer); | |
queryItem.appendChild(searchLoading); | |
queryItem.appendChild(resultsTable); | |
// Add container to the queries list | |
queriesContainer.appendChild(queryItem); | |
} | |
// Perform search and update the results table | |
function performSearch(query, queryItemElement) { | |
if (!query.trim()) { | |
alert('Please enter a search query'); | |
return; | |
} | |
const loadingElement = queryItemElement.querySelector('.search-loading'); | |
const tableElement = queryItemElement.querySelector('.results-table'); | |
const tableBody = tableElement.querySelector('tbody'); | |
// Show loading and hide previous results | |
loadingElement.style.display = 'block'; | |
tableElement.style.display = 'none'; | |
// Clear previous results | |
tableBody.innerHTML = ''; | |
// Get checkbox values | |
const pdfChecked = document.getElementById('pdfOption').checked; | |
const patentChecked = document.getElementById('patentOption').checked; | |
const webChecked = document.getElementById('webOption').checked; | |
// Create form data with query and checkbox values | |
const formData = new FormData(); | |
formData.append('query', query); | |
formData.append('pdfOption', pdfChecked); | |
formData.append('patentOption', patentChecked); | |
formData.append('webOption', webChecked); | |
// Send search request to backend | |
fetch('/search', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
loadingElement.style.display = 'none'; | |
if (data.results && data.results.length > 0) { | |
// Populate table with results | |
data.results.forEach(result => { | |
const row = document.createElement('tr'); | |
const typeCell = document.createElement('td'); | |
typeCell.textContent = result.type; | |
const titleCell = document.createElement('td'); | |
titleCell.textContent = result.title; | |
const bodyCell = document.createElement('td'); | |
bodyCell.textContent = result.body; | |
const urlCell = document.createElement('td'); | |
const urlLink = document.createElement('a'); | |
urlLink.href = result.url; | |
urlLink.textContent = result.url; | |
urlLink.className = 'url-link'; | |
urlLink.target = '_blank'; | |
urlCell.appendChild(urlLink); | |
// Add "Analyze" button to the URL cell | |
const analyzeButton = document.createElement('button'); | |
analyzeButton.textContent = 'Analyze'; | |
analyzeButton.className = 'action-button analyze-button'; | |
analyzeButton.onclick = function(e) { | |
e.preventDefault(); | |
analyzePaper(result.url, queryItemElement); | |
}; | |
urlCell.appendChild(document.createElement('br')); | |
urlCell.appendChild(analyzeButton); | |
row.appendChild(typeCell); | |
row.appendChild(titleCell); | |
row.appendChild(bodyCell); | |
row.appendChild(urlCell); | |
tableBody.appendChild(row); | |
}); | |
// Show the table | |
tableElement.style.display = 'table'; | |
} else { | |
// No results | |
const row = document.createElement('tr'); | |
const cell = document.createElement('td'); | |
cell.colSpan = 4; // Updated to 4 since we now have 4 columns with the type column | |
cell.textContent = 'No results found.'; | |
cell.style.textAlign = 'center'; | |
row.appendChild(cell); | |
tableBody.appendChild(row); | |
tableElement.style.display = 'table'; | |
} | |
}) | |
.catch(error => { | |
loadingElement.style.display = 'none'; | |
console.error('Error:', error); | |
// Show error in table | |
const row = document.createElement('tr'); | |
const cell = document.createElement('td'); | |
cell.colSpan = 4; // Updated to 4 since we now have 4 columns with the type column | |
cell.textContent = 'Error performing search. Please try again.'; | |
cell.style.textAlign = 'center'; | |
cell.style.color = 'red'; | |
row.appendChild(cell); | |
tableBody.appendChild(row); | |
tableElement.style.display = 'table'; | |
}); | |
} | |
// Extract insights from document | |
function extractInsights(paperUrl, row) { | |
const patentBackground = document.getElementById('userInput').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
const documentType = row.cells[0].textContent.toLowerCase(); | |
// Check if there's already an insights row for this document | |
let insightsRow = row.nextElementSibling; | |
if (insightsRow && insightsRow.className === 'insights-row') { | |
// Insights row already exists, get the container | |
insightsContainer = insightsRow.querySelector('.insights-container'); | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
} else { | |
// Create insights row that spans across all columns | |
insightsRow = document.createElement('tr'); | |
insightsRow.className = 'insights-row'; | |
const insightsTd = document.createElement('td'); | |
insightsTd.colSpan = row.cells.length; | |
insightsContainer = document.createElement('div'); | |
insightsContainer.className = 'insights-container'; | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
insightsTd.appendChild(insightsContainer); | |
insightsRow.appendChild(insightsTd); | |
// Insert after the current row | |
row.parentNode.insertBefore(insightsRow, row.nextSibling); | |
} | |
// Store the row reference as a data attribute on the container for later access | |
insightsContainer.dataset.parentRow = row.rowIndex; | |
// Send request to extract insights | |
fetch('/post_insights', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': paperUrl, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
insightsContainer.innerHTML = `<div class="error">Error: ${data.error}</div>`; | |
} else if (data.result && data.result.insights) { | |
// Create header with title and actions | |
const insightsHeader = document.createElement('div'); | |
insightsHeader.className = 'insights-header'; | |
const insightsTitle = document.createElement('div'); | |
insightsTitle.className = 'insights-title'; | |
insightsTitle.textContent = 'Document Insights'; | |
const insightsActions = document.createElement('div'); | |
insightsActions.className = 'insights-actions'; | |
// Store the insights for this specific container | |
const containerInsights = [...data.result.insights]; | |
insightsContainer.dataset.allInsights = JSON.stringify(containerInsights); | |
const selectAllBtn = document.createElement('button'); | |
selectAllBtn.className = 'insights-button select-all-btn'; | |
selectAllBtn.textContent = 'Select All'; | |
// Use a closure to ensure the button has access to the correct container and row | |
selectAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.add('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify(allInsights); | |
parentRow.dataset.unselectedInsights = JSON.stringify([]); | |
// Trigger check for enabling/disabling enhance button | |
checkSelectedInsights(); | |
}); | |
const clearAllBtn = document.createElement('button'); | |
clearAllBtn.className = 'insights-button clear-all-btn'; | |
clearAllBtn.textContent = 'Clear All'; | |
// Use a closure to ensure the button has access to the correct container and row | |
clearAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.remove('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify([]); | |
parentRow.dataset.unselectedInsights = JSON.stringify(allInsights); | |
// Trigger check for enabling/disabling enhance button | |
checkSelectedInsights(); | |
}); | |
insightsActions.appendChild(selectAllBtn); | |
insightsActions.appendChild(clearAllBtn); | |
insightsHeader.appendChild(insightsTitle); | |
insightsHeader.appendChild(insightsActions); | |
// Create insight tags | |
const insightsList = document.createElement('div'); | |
insightsList.className = 'insights-list'; | |
// Clear the container and add the new elements | |
insightsContainer.innerHTML = ''; | |
insightsContainer.appendChild(insightsHeader); | |
insightsContainer.appendChild(insightsList); | |
// Initialize selected insights | |
const selectedInsights = []; | |
const unselectedInsights = []; | |
// Add insight tags | |
data.result.insights.forEach(insight => { | |
const tagElement = document.createElement('div'); | |
tagElement.className = 'insight-tag'; | |
tagElement.textContent = insight; | |
tagElement.addEventListener('click', function() { | |
this.classList.toggle('selected'); | |
// Update selected insights | |
updateSelectedInsights(row, insightsContainer); | |
// Trigger check for enabling/disabling enhance button | |
checkSelectedInsights(); | |
}); | |
// Add to unselected by default | |
// tagElement.classList.add('selected'); | |
unselectedInsights.push(insight); | |
insightsList.appendChild(tagElement); | |
}); | |
// Store initial selections | |
row.dataset.selectedInsights = JSON.stringify(selectedInsights); | |
row.dataset.unselectedInsights = JSON.stringify(unselectedInsights); | |
// Trigger check for enabling/disabling enhance button immediately | |
checkSelectedInsights(); | |
} else { | |
insightsContainer.innerHTML = '<div class="error">No insights found</div>'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
insightsContainer.innerHTML = `<div class="error">Error extracting insights: ${error.message}</div>`; | |
}); | |
} | |
// Helper function to update selected insights stored in the row's dataset | |
function updateSelectedInsights(row, insightsContainer) { | |
const selectedInsights = []; | |
const unselectedInsights = []; | |
const tags = insightsContainer.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => { | |
if (tag.classList.contains('selected')) { | |
selectedInsights.push(tag.textContent); | |
} else { | |
unselectedInsights.push(tag.textContent); | |
} | |
}); | |
row.dataset.selectedInsights = JSON.stringify(selectedInsights); | |
row.dataset.unselectedInsights = JSON.stringify(unselectedInsights); | |
} | |
// Collect all selected insights from all rows | |
function collectAllSelectedInsights() { | |
const allSelectedInsights = []; | |
const queryItems = document.querySelectorAll('.query-item'); | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
if (row.dataset.selectedInsights) { | |
try { | |
const selectedInsights = JSON.parse(row.dataset.selectedInsights); | |
selectedInsights.forEach(insight => { | |
if (!allSelectedInsights.includes(insight)) { | |
allSelectedInsights.push(insight); | |
} | |
}); | |
} catch (e) { | |
console.error('Error parsing selected insights:', e); | |
} | |
} | |
}); | |
}); | |
return allSelectedInsights; | |
} | |
// Enhance the problem using the selected insights | |
function enhanceProblem() { | |
const problemInput = document.getElementById('userInput'); | |
const originalProblem = problemInput.value.trim(); | |
if (!originalProblem) { | |
alert('Please provide a technical problem description first'); | |
return; | |
} | |
const selectedInsights = collectAllSelectedInsights(); | |
if (selectedInsights.length === 0) { | |
alert('Please select at least one insight to enhance the problem'); | |
return; | |
} | |
// Get user comments if any | |
const userComments = document.getElementById('insightCommentArea')?.value || ''; | |
// If we have stored refined problems for this exact problem, just show them | |
const currentProblemKey = JSON.stringify({ | |
problem: originalProblem, | |
insights: selectedInsights.sort(), | |
comments: userComments | |
}); | |
if (storedRefinedProblems[currentProblemKey]) { | |
showRefinedProblems(storedRefinedProblems[currentProblemKey]); | |
return; | |
} | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'Enhancing problem statement...'; | |
} | |
// Send request to backend to refine the problem | |
fetch('/refine-problem', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'original_problem': originalProblem, | |
'insights': selectedInsights, | |
'user_comments': userComments | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading overlay | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
if (data.error) { | |
alert(`Error: ${data.error}`); | |
return; | |
} | |
if (data.result) { | |
// Add current problem to history if not already there | |
if (currentHistoryIndex === -1) { | |
problemHistory.push(originalProblem); | |
currentHistoryIndex = 0; | |
} | |
// Store the refined problems for future use | |
storedRefinedProblems[currentProblemKey] = data.result; | |
// Show refined problem options | |
showRefinedProblems(data.result); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error enhancing problem: ${error.message}`); | |
}); | |
} | |
// Display the refined problem options | |
function showRefinedProblems(result) { | |
const refinedContainer = document.getElementById('refinedProblemContainer'); | |
const tabs = document.getElementById('refinedProblemTabs'); | |
const content = document.getElementById('refinedProblemContent'); | |
// Clear previous content | |
tabs.innerHTML = ''; | |
content.innerHTML = ''; | |
// Add tabs and content for each refined problem | |
let isFirst = true; | |
for (const key in result) { | |
if (result.hasOwnProperty(key)) { | |
const problem = result[key]; | |
// Create tab | |
const tab = document.createElement('div'); | |
tab.className = `refined-problem-tab ${isFirst ? 'active' : ''}`; | |
tab.dataset.target = key; | |
tab.textContent = `Option ${key.split('_')[1]}`; | |
tab.onclick = function() { | |
document.querySelectorAll('.refined-problem-tab').forEach(t => t.classList.remove('active')); | |
document.querySelectorAll('.refined-problem').forEach(p => p.classList.remove('active')); | |
tab.classList.add('active'); | |
document.getElementById(key).classList.add('active'); | |
}; | |
tabs.appendChild(tab); | |
// Create content | |
const problemDiv = document.createElement('div'); | |
problemDiv.className = `refined-problem ${isFirst ? 'active' : ''}`; | |
problemDiv.id = key; | |
const title = document.createElement('div'); | |
title.className = 'refined-problem-title'; | |
title.textContent = problem.title; | |
const description = document.createElement('div'); | |
description.className = 'refined-problem-description'; | |
description.textContent = problem.description; | |
const applyButton = document.createElement('button'); | |
applyButton.className = 'apply-problem-btn'; | |
applyButton.textContent = 'Apply This Problem'; | |
applyButton.onclick = function() { | |
applyRefinedProblem(problem.description); | |
}; | |
problemDiv.appendChild(title); | |
problemDiv.appendChild(description); | |
problemDiv.appendChild(applyButton); | |
content.appendChild(problemDiv); | |
isFirst = false; | |
} | |
} | |
// Show the container | |
refinedContainer.style.display = 'block'; | |
// Scroll to the container | |
refinedContainer.scrollIntoView({ behavior: 'smooth' }); | |
} | |
// Apply the selected refined problem | |
function applyRefinedProblem(problemText) { | |
const problemInput = document.getElementById('userInput'); | |
// Add current problem to history | |
problemHistory.push(problemInput.value); | |
currentHistoryIndex = problemHistory.length - 1; // Set to latest version | |
// Update problem text with the new version | |
problemInput.value = problemText; | |
// Now add the new version to history | |
problemHistory.push(problemText); | |
currentHistoryIndex = problemHistory.length - 1; // Update index to point to the newly added version | |
// Update history navigation display | |
updateHistoryNavigation(); | |
// Display is kept visible, removed: document.getElementById('refinedProblemContainer').style.display = 'none'; | |
// Focus on the problem input | |
problemInput.focus(); | |
console.log("Problem history after applying:", problemHistory, "Current index:", currentHistoryIndex); | |
} | |
// Update the history navigation UI | |
function updateHistoryNavigation() { | |
const historyNav = document.getElementById('problemHistoryNav'); | |
const prevBtn = historyNav.querySelector('.history-prev'); | |
const nextBtn = historyNav.querySelector('.history-next'); | |
const status = historyNav.querySelector('.history-status'); | |
// Update buttons state | |
prevBtn.classList.toggle('disabled', currentHistoryIndex <= 0); | |
nextBtn.classList.toggle('disabled', currentHistoryIndex >= problemHistory.length - 1); | |
// Update status text | |
if (problemHistory.length > 0) { | |
status.textContent = `Version ${currentHistoryIndex + 1} of ${problemHistory.length}`; | |
historyNav.style.display = 'flex'; | |
} else { | |
historyNav.style.display = 'none'; | |
} | |
} | |
// Navigate to previous problem version | |
function navigateProblemHistory(direction) { | |
const problemInput = document.getElementById('userInput'); | |
console.log("Current history index:", currentHistoryIndex, "Total history:", problemHistory.length); | |
if (direction === 'prev' && currentHistoryIndex > 0) { | |
currentHistoryIndex--; | |
problemInput.value = problemHistory[currentHistoryIndex]; | |
console.log("Moving to previous version:", currentHistoryIndex); | |
} else if (direction === 'next' && currentHistoryIndex < problemHistory.length - 1) { | |
currentHistoryIndex++; | |
problemInput.value = problemHistory[currentHistoryIndex]; | |
console.log("Moving to next version:", currentHistoryIndex); | |
} | |
updateHistoryNavigation(); | |
} | |
function analyzePaper(paperUrl, queryItemElement) { | |
const patentBackground = document.getElementById('userInput').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
// Find the row containing this URL | |
const rows = queryItemElement.querySelectorAll('tbody tr'); | |
let targetRow; | |
let documentType = 'pdf'; // Default type | |
for (const row of rows) { | |
const urlLink = row.querySelector('.url-link'); | |
if (urlLink && urlLink.href === paperUrl) { | |
targetRow = row; | |
// Get the document type from the first cell of the row | |
documentType = row.cells[0].textContent.toLowerCase(); | |
break; | |
} | |
} | |
if (!targetRow) { | |
alert('Could not find the paper in the results table'); | |
return; | |
} | |
// Check if we already have analysis columns | |
let scoreCell, justificationCell; | |
if (targetRow.cells.length <= 4) { | |
// We need to create the cells | |
scoreCell = document.createElement('td'); | |
scoreCell.className = 'score-cell'; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell = document.createElement('td'); | |
justificationCell.className = 'justification-cell'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
targetRow.appendChild(scoreCell); | |
targetRow.appendChild(justificationCell); | |
} else { | |
// Use existing cells | |
scoreCell = targetRow.cells[4]; | |
justificationCell = targetRow.cells[5]; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
} | |
// Send analysis request to backend | |
fetch('/analyze', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': paperUrl, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = data.error; | |
} else if (data.result) { | |
// Get score and justification from the result | |
const result = data.result; | |
const score = result.score !== undefined ? result.score : 'N/A'; | |
const justification = result.justification || 'No justification provided'; | |
// Update cells with results | |
scoreCell.innerHTML = score; | |
justificationCell.innerHTML = justification; | |
// Color-code the score | |
if (score >= 0 && score <= 5) { | |
const redComponent = Math.floor((score / 5) * 255); | |
const greenComponent = Math.floor(((5 - score) / 5) * 255); | |
scoreCell.style.backgroundColor = `rgb(${redComponent}, ${greenComponent}, 0)`; | |
scoreCell.style.color = 'white'; | |
scoreCell.style.fontWeight = 'bold'; | |
scoreCell.style.textAlign = 'center'; | |
} | |
// Add "Extract Insights" button to the justify cell | |
const insightsButton = document.createElement('button'); | |
insightsButton.textContent = 'Extract Insights'; | |
insightsButton.className = 'action-button insights-button'; | |
insightsButton.style.marginTop = '5px'; | |
insightsButton.style.fontSize = '0.8em'; | |
insightsButton.onclick = function(e) { | |
e.preventDefault(); | |
extractInsights(paperUrl, targetRow); | |
}; | |
justificationCell.appendChild(document.createElement('br')); | |
justificationCell.appendChild(insightsButton); | |
} else { | |
scoreCell.innerHTML = 'No data'; | |
justificationCell.innerHTML = 'Analysis failed'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = 'Failed to analyze paper'; | |
}); | |
} | |
// Update the placeholder text numbers after deletions | |
function updateQueryIndices() { | |
const queryItems = document.getElementById('queriesContainer').children; | |
for (let i = 0; i < queryItems.length; i++) { | |
const queryField = queryItems[i].querySelector('.query-field'); | |
queryField.placeholder = `Search Query ${i + 1}`; | |
} | |
} | |
// Get all queries as an array of strings | |
function getQueries() { | |
const queryFields = document.querySelectorAll('.query-field'); | |
return Array.from(queryFields).map(field => field.value.trim()); | |
} | |
// Analyze all unanalyzed papers in the results | |
function analyzeAllPapers() { | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) loadingOverlay.style.display = 'flex'; | |
// Get all query items | |
const queryItems = document.querySelectorAll('.query-item'); | |
let totalToAnalyze = 0; | |
let completedAnalyses = 0; | |
// Count total papers that need analysis | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr'); | |
rows.forEach(row => { | |
if (row.cells.length <= 4 || row.cells[4].textContent.trim() === 'Error' || | |
row.cells[4].textContent.trim() === 'N/A' || row.cells[4].textContent.trim() === '') { | |
totalToAnalyze++; | |
} | |
}); | |
}); | |
if (totalToAnalyze === 0) { | |
alert('No papers need analysis'); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
return; | |
} | |
// Function to update progress | |
function updateProgress() { | |
completedAnalyses++; | |
if (loadingOverlay) { | |
const progressElement = loadingOverlay.querySelector('.progress-text'); | |
if (progressElement) { | |
progressElement.textContent = `Analyzing papers: ${completedAnalyses}/${totalToAnalyze}`; | |
} | |
} | |
if (completedAnalyses >= totalToAnalyze) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
} | |
} | |
// Analyze each paper that needs it | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr'); | |
rows.forEach(row => { | |
// Check if this row needs analysis | |
if (row.cells.length <= 4 || row.cells[4].textContent.trim() === 'Error' || | |
row.cells[4].textContent.trim() === 'N/A' || row.cells[4].textContent.trim() === '') { | |
// Get the URL | |
const urlLink = row.querySelector('.url-link'); | |
const documentType = row.cells[0].textContent.toLowerCase(); | |
if (urlLink && urlLink.href) { | |
// Create a promise for this analysis | |
const promise = new Promise((resolve) => { | |
// Prepare for analysis | |
const patentBackground = document.getElementById('userInput').value.trim(); | |
// Create score and justification cells if needed | |
let scoreCell, justificationCell; | |
if (row.cells.length <= 4) { | |
scoreCell = document.createElement('td'); | |
scoreCell.className = 'score-cell'; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell = document.createElement('td'); | |
justificationCell.className = 'justification-cell'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
row.appendChild(scoreCell); | |
row.appendChild(justificationCell); | |
} else { | |
scoreCell = row.cells[4]; | |
justificationCell = row.cells[5]; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
} | |
// Send analysis request | |
fetch('/analyze', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': urlLink.href, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = data.error; | |
} else if (data.result) { | |
// Get score and justification | |
const result = data.result; | |
const score = result.score !== undefined ? result.score : 'N/A'; | |
const justification = result.justification || 'No justification provided'; | |
// Update cells | |
scoreCell.innerHTML = score; | |
justificationCell.innerHTML = justification; | |
// Color-code score | |
if (score >= 0 && score <= 5) { | |
const redComponent = Math.floor((score / 5) * 255); | |
const greenComponent = Math.floor(((5 - score) / 5) * 255); | |
scoreCell.style.backgroundColor = `rgb(${redComponent}, ${greenComponent}, 0)`; | |
scoreCell.style.color = 'white'; | |
scoreCell.style.fontWeight = 'bold'; | |
scoreCell.style.textAlign = 'center'; | |
} | |
// Add "Extract Insights" button to the justify cell - for Analyze All function | |
const insightsButton = document.createElement('button'); | |
insightsButton.textContent = 'Extract Insights'; | |
insightsButton.className = 'action-button insights-button'; | |
insightsButton.style.marginTop = '5px'; | |
insightsButton.style.fontSize = '0.8em'; | |
insightsButton.onclick = function(e) { | |
e.preventDefault(); | |
extractInsights(urlLink.href, row); | |
}; | |
justificationCell.appendChild(document.createElement('br')); | |
justificationCell.appendChild(insightsButton); | |
} else { | |
scoreCell.innerHTML = 'No data'; | |
justificationCell.innerHTML = 'Analysis failed'; | |
} | |
resolve(); | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = 'Failed to analyze paper'; | |
resolve(); | |
}); | |
}); | |
// When this analysis is done, update the progress | |
promise.then(() => { | |
updateProgress(); | |
}); | |
} else { | |
// No URL found, count as completed | |
updateProgress(); | |
} | |
} | |
}); | |
}); | |
} | |
// Remove rows with failed analyses | |
function removeFailedAnalyses() { | |
const queryItems = document.querySelectorAll('.query-item'); | |
let removedCount = 0; | |
queryItems.forEach(queryItem => { | |
const tbody = queryItem.querySelector('tbody'); | |
const rows = Array.from(tbody.querySelectorAll('tr')); | |
rows.forEach(row => { | |
if (row.cells.length > 4) { | |
const scoreText = row.cells[4].textContent.trim(); | |
if (scoreText === 'Error' || scoreText === 'N/A' || scoreText === 'No data') { | |
tbody.removeChild(row); | |
removedCount++; | |
} | |
} | |
}); | |
}); | |
// Create and display a simple notification instead of alert | |
const notification = document.createElement('div'); | |
notification.className = 'simple-notification'; | |
notification.textContent = `Removed ${removedCount} papers with failed analyses`; | |
notification.style.position = 'fixed'; | |
notification.style.top = '20px'; | |
notification.style.right = '20px'; | |
notification.style.background = '#4CAF50'; | |
notification.style.color = 'white'; | |
notification.style.padding = '15px'; | |
notification.style.borderRadius = '5px'; | |
notification.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'; | |
notification.style.zIndex = '10000'; | |
// Add notification to the body | |
document.body.appendChild(notification); | |
// Remove notification after 3 seconds | |
setTimeout(() => { | |
if (notification.parentNode) { | |
notification.parentNode.removeChild(notification); | |
} | |
}, 3000); | |
} | |
// Export all tables to Excel | |
function exportToExcel() { | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'Generating Excel file...'; | |
} | |
try { | |
// Collect all data from all tables | |
const allData = []; | |
const queryItems = document.querySelectorAll('.query-item'); | |
queryItems.forEach(queryItem => { | |
// Get the search query text for this table | |
const queryText = queryItem.querySelector('.query-field').value; | |
// Get all rows in this table | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Skip empty rows or error rows | |
if (row.cells.length === 1 && row.cells[0].colSpan > 1) { | |
return; // Skip "No results found" rows | |
} | |
// Prepare object for this row | |
const rowData = { | |
'Topic': queryText, | |
'Type': row.cells[0].textContent, | |
'Title': row.cells[1].textContent, | |
'Description': row.cells[2].textContent, | |
'URL': row.querySelector('.url-link')?.href || '' | |
}; | |
// Add score and justification if they exist | |
if (row.cells.length > 4) { | |
rowData['Score'] = row.cells[4].textContent; | |
rowData['Justification'] = row.cells[5].textContent.split('\n')[0]; // Only get the justification text | |
} else { | |
rowData['Score'] = ''; | |
rowData['Justification'] = ''; | |
} | |
// Add selected and unselected insights if they exist | |
if (row.dataset.selectedInsights) { | |
try { | |
const selectedInsights = JSON.parse(row.dataset.selectedInsights); | |
rowData['Selected Insights'] = selectedInsights.join('; '); | |
} catch (e) { | |
rowData['Selected Insights'] = ''; | |
} | |
} else { | |
rowData['Selected Insights'] = ''; | |
} | |
if (row.dataset.unselectedInsights) { | |
try { | |
const unselectedInsights = JSON.parse(row.dataset.unselectedInsights); | |
rowData['Unselected Insights'] = unselectedInsights.join('; '); | |
} catch (e) { | |
rowData['Unselected Insights'] = ''; | |
} | |
} else { | |
rowData['Unselected Insights'] = ''; | |
} | |
allData.push(rowData); | |
}); | |
}); | |
// Check if we have any data | |
if (allData.length === 0) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert('No data to export. Please perform a search first.'); | |
return; | |
} | |
// Get all problem versions | |
const problemVersions = {}; | |
if (problemHistory.length > 0) { | |
problemHistory.forEach((problem, index) => { | |
problemVersions[`Problem Version ${index + 1}`] = problem; | |
}); | |
} else { | |
// If no history, just use the current problem | |
const currentProblem = document.getElementById('userInput').value.trim(); | |
if (currentProblem) { | |
problemVersions['Problem Version 1'] = currentProblem; | |
} | |
} | |
// Send to server for Excel generation | |
fetch('/export-excel', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'tableData': allData, | |
'userQuery': document.getElementById('userInput').value, | |
'problemVersions': problemVersions | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`Server error: ${response.status}`); | |
} | |
return response.blob(); | |
}) | |
.then(blob => { | |
// Hide loading overlay | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
// Create URL for the blob | |
const url = window.URL.createObjectURL(new Blob([blob], { | |
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | |
})); | |
// Create a link and trigger download | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'patent_search_results.xlsx'; | |
document.body.appendChild(a); | |
a.click(); | |
// Clean up | |
window.URL.revokeObjectURL(url); | |
document.body.removeChild(a); | |
}) | |
.catch(error => { | |
console.error('Error exporting to Excel:', error); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error exporting to Excel: ${error.message}`); | |
}); | |
} catch (error) { | |
console.error('Error preparing Excel data:', error); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error preparing data for export: ${error.message}`); | |
} | |
} | |
// Check if any insights are selected to enable/disable enhance button | |
function checkSelectedInsights() { | |
const selectedInsights = collectAllSelectedInsights(); | |
const enhanceButton = document.getElementById('enhanceProblemButton'); | |
if (selectedInsights.length > 0) { | |
enhanceButton.classList.remove('disabled'); | |
enhanceButton.disabled = false; | |
} else { | |
enhanceButton.classList.add('disabled'); | |
enhanceButton.disabled = true; | |
} | |
} | |
// Initialize comment area for insights | |
function initializeCommentArea() { | |
const container = document.getElementById('insightCommentContainer'); | |
if (!container) return; | |
const textarea = document.createElement('textarea'); | |
textarea.id = 'insightCommentArea'; | |
textarea.className = 'insight-comment-textarea'; | |
textarea.placeholder = 'Add additional suggestions for enhancing the problem (optional)...'; | |
container.innerHTML = ''; | |
container.appendChild(textarea); | |
} | |
// Create a new accordion section for search results | |
function createAccordionSection(timestamp) { | |
const accordionSection = document.createElement('div'); | |
accordionSection.className = 'accordion-section'; | |
const accordionHeader = document.createElement('div'); | |
accordionHeader.className = 'accordion-header'; | |
accordionHeader.innerHTML = ` | |
<span>Search Results <span class="query-timestamp">${timestamp}</span></span> | |
<span class="toggle-icon">▼</span> | |
`; | |
accordionHeader.onclick = function() { | |
this.classList.toggle('collapsed'); | |
const body = this.nextElementSibling; | |
body.classList.toggle('collapsed'); | |
// Update the toggle icon | |
const toggleIcon = this.querySelector('.toggle-icon'); | |
if (body.classList.contains('collapsed')) { | |
toggleIcon.textContent = '▶'; | |
} else { | |
toggleIcon.textContent = '▼'; | |
} | |
}; | |
const accordionBody = document.createElement('div'); | |
accordionBody.className = 'accordion-body'; | |
const accordionContent = document.createElement('div'); | |
accordionContent.className = 'accordion-content'; | |
accordionBody.appendChild(accordionContent); | |
accordionSection.appendChild(accordionHeader); | |
accordionSection.appendChild(accordionBody); | |
return accordionSection; | |
} | |
// Extract insights from all analyzed documents | |
function extractAllInsights() { | |
const patentBackground = document.getElementById('userInput').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'Extracting insights from all documents...'; | |
} | |
// Get all query items | |
const queryItems = document.querySelectorAll('.query-item'); | |
let totalDocuments = 0; | |
let completedDocuments = 0; | |
// Count total analyzed documents that need insights extraction | |
const analyzedRows = []; | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Only include rows that have been analyzed (have score and justification cells) | |
if (row.cells.length > 4 && row.cells[4].textContent.trim() !== 'Error' && | |
row.cells[4].textContent.trim() !== 'N/A' && row.cells[4].textContent.trim() !== '') { | |
analyzedRows.push(row); | |
totalDocuments++; | |
} | |
}); | |
}); | |
if (totalDocuments === 0) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert('No analyzed documents found. Please analyze documents first.'); | |
return; | |
} | |
// Function to update progress | |
function updateProgress() { | |
completedDocuments++; | |
if (loadingOverlay) { | |
const progressElement = loadingOverlay.querySelector('.progress-text'); | |
if (progressElement) { | |
progressElement.textContent = `Extracting insights: ${completedDocuments}/${totalDocuments}`; | |
} | |
} | |
if (completedDocuments >= totalDocuments) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
checkSelectedInsights(); // Update enhance button state based on selected insights | |
} | |
} | |
// Process each analyzed document sequentially to avoid overwhelming the server | |
function processNextDocument(index) { | |
if (index >= analyzedRows.length) { | |
return; // All documents processed | |
} | |
const row = analyzedRows[index]; | |
const urlLink = row.querySelector('.url-link'); | |
const documentType = row.cells[0].textContent.toLowerCase(); | |
if (!urlLink || !urlLink.href) { | |
updateProgress(); | |
processNextDocument(index + 1); | |
return; | |
} | |
// Check if there's already an insights row for this document | |
let insightsRow = row.nextElementSibling; | |
if (insightsRow && insightsRow.className === 'insights-row') { | |
// Insights row already exists, get the container | |
insightsContainer = insightsRow.querySelector('.insights-container'); | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
} else { | |
// Create insights row that spans across all columns | |
insightsRow = document.createElement('tr'); | |
insightsRow.className = 'insights-row'; | |
const insightsTd = document.createElement('td'); | |
insightsTd.colSpan = row.cells.length; | |
insightsContainer = document.createElement('div'); | |
insightsContainer.className = 'insights-container'; | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
insightsTd.appendChild(insightsContainer); | |
insightsRow.appendChild(insightsTd); | |
// Insert after the current row | |
row.parentNode.insertBefore(insightsRow, row.nextSibling); | |
} | |
// Store the row reference as a data attribute on the container for later access | |
insightsContainer.dataset.parentRow = row.rowIndex; | |
// Send request to extract insights | |
fetch('/post_insights', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': urlLink.href, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
insightsContainer.innerHTML = `<div class="error">Error: ${data.error}</div>`; | |
} else if (data.result && data.result.insights) { | |
// Create header with title and actions | |
const insightsHeader = document.createElement('div'); | |
insightsHeader.className = 'insights-header'; | |
const insightsTitle = document.createElement('div'); | |
insightsTitle.className = 'insights-title'; | |
insightsTitle.textContent = 'Document Insights'; | |
const insightsActions = document.createElement('div'); | |
insightsActions.className = 'insights-actions'; | |
// Store the insights for this specific container | |
const containerInsights = [...data.result.insights]; | |
insightsContainer.dataset.allInsights = JSON.stringify(containerInsights); | |
const selectAllBtn = document.createElement('button'); | |
selectAllBtn.className = 'insights-button select-all-btn'; | |
selectAllBtn.textContent = 'Select All'; | |
selectAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.add('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify(allInsights); | |
parentRow.dataset.unselectedInsights = JSON.stringify([]); | |
checkSelectedInsights(); | |
}); | |
const clearAllBtn = document.createElement('button'); | |
clearAllBtn.className = 'insights-button clear-all-btn'; | |
clearAllBtn.textContent = 'Clear All'; | |
clearAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.remove('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify([]); | |
parentRow.dataset.unselectedInsights = JSON.stringify(allInsights); | |
checkSelectedInsights(); | |
}); | |
insightsActions.appendChild(selectAllBtn); | |
insightsActions.appendChild(clearAllBtn); | |
insightsHeader.appendChild(insightsTitle); | |
insightsHeader.appendChild(insightsActions); | |
// Create insight tags | |
const insightsList = document.createElement('div'); | |
insightsList.className = 'insights-list'; | |
// Clear the container and add the new elements | |
insightsContainer.innerHTML = ''; | |
insightsContainer.appendChild(insightsHeader); | |
insightsContainer.appendChild(insightsList); | |
// Initialize selected insights | |
const selectedInsights = []; | |
const unselectedInsights = []; | |
// Add insight tags | |
data.result.insights.forEach(insight => { | |
const tagElement = document.createElement('div'); | |
tagElement.className = 'insight-tag'; | |
tagElement.textContent = insight; | |
tagElement.addEventListener('click', function() { | |
this.classList.toggle('selected'); | |
updateSelectedInsights(row, insightsContainer); | |
checkSelectedInsights(); | |
}); | |
// Add to unselected by default | |
// tagElement.classList.add('selected'); | |
unselectedInsights.push(insight); | |
insightsList.appendChild(tagElement); | |
}); | |
// Store initial selections | |
row.dataset.selectedInsights = JSON.stringify(selectedInsights); | |
row.dataset.unselectedInsights = JSON.stringify(unselectedInsights); | |
} else { | |
insightsContainer.innerHTML = '<div class="error">No insights found</div>'; | |
} | |
// Process next document | |
updateProgress(); | |
processNextDocument(index + 1); | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
insightsContainer.innerHTML = `<div class="error">Error extracting insights: ${error.message}</div>`; | |
// Continue with next document even if this one failed | |
updateProgress(); | |
processNextDocument(index + 1); | |
}); | |
} | |
// Start processing documents | |
processNextDocument(0); | |
} | |
// Check if any documents have been analyzed to enable/disable Extract All Insights button | |
function checkAnalyzedDocuments() { | |
const queryItems = document.querySelectorAll('.query-item'); | |
let hasAnalyzedDocuments = false; | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Check if the row has score and justification cells (has been analyzed) | |
if (row.cells.length > 4 && row.cells[4].textContent.trim() !== 'Error' && | |
row.cells[4].textContent.trim() !== '') { | |
hasAnalyzedDocuments = true; | |
} | |
}); | |
}); | |
const extractAllButton = document.getElementById('extractAllInsightsButton'); | |
if (hasAnalyzedDocuments) { | |
extractAllButton.classList.remove('disabled'); | |
extractAllButton.disabled = false; | |
} else { | |
extractAllButton.classList.add('disabled'); | |
extractAllButton.disabled = true; | |
} | |
} | |
// Group insights by score and display in a consolidated table | |
function groupInsightsByScore() { | |
// Get all query items | |
const queryItems = document.querySelectorAll('.query-item'); | |
let hasInsights = false; | |
// Create structure to hold insights grouped by score | |
const insightsByScore = { | |
5: { insights: [], sources: [] }, | |
4: { insights: [], sources: [] }, | |
3: { insights: [], sources: [] }, | |
2: { insights: [], sources: [] }, | |
1: { insights: [], sources: [] }, | |
0: { insights: [], sources: [] } | |
}; | |
// Collect all insights from all documents and group by score | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Skip if no score or insights | |
if (row.cells.length <= 4 || !row.dataset.selectedInsights) { | |
return; | |
} | |
// Get the score | |
const scoreText = row.cells[4].textContent.trim(); | |
if (scoreText === 'Error' || scoreText === 'N/A' || isNaN(parseFloat(scoreText))) { | |
return; | |
} | |
const score = Math.round(parseFloat(scoreText)); | |
const urlLink = row.querySelector('.url-link'); | |
if (!urlLink) return; | |
// Get insights from this document | |
try { | |
const selectedInsights = JSON.parse(row.dataset.selectedInsights || '[]'); | |
const unselectedInsights = JSON.parse(row.dataset.unselectedInsights || '[]'); | |
const allInsights = [...selectedInsights, ...unselectedInsights]; | |
// Add insights to the appropriate score group | |
if (score >= 0 && score <= 5 && allInsights.length > 0) { | |
hasInsights = true; | |
// For all insights in this document | |
allInsights.forEach(insight => { | |
// Check if this insight is already in the group | |
const existingIndex = insightsByScore[score].insights.findIndex(i => i === insight); | |
if (existingIndex === -1) { | |
// New insight | |
insightsByScore[score].insights.push(insight); | |
insightsByScore[score].sources.push({ | |
url: urlLink.href, | |
title: row.cells[1].textContent.trim().substring(0, 30) + '...', | |
selected: selectedInsights.includes(insight) | |
}); | |
} | |
}); | |
} | |
} catch (e) { | |
console.error('Error parsing insights:', e); | |
} | |
}); | |
}); | |
if (!hasInsights) { | |
alert('No insights found. Please analyze documents and extract insights first.'); | |
return; | |
} | |
// Create or get the grouped insights container | |
let container = document.getElementById('groupedInsightsContainer'); | |
if (!container) { | |
container = document.createElement('div'); | |
container.id = 'groupedInsightsContainer'; | |
container.className = 'grouped-insights-container'; | |
// Insert after the problem input card | |
const card = document.querySelector('.card'); | |
card.parentNode.insertBefore(container, card.nextSibling); | |
} | |
// Create header | |
const header = document.createElement('div'); | |
header.className = 'grouped-insights-header'; | |
header.innerHTML = ` | |
<div>Insights Grouped by Score</div> | |
<div class="grouped-insights-actions"> | |
<button class="ai-select-btn pulse" id="aiSelectButton" onclick="aiSelectInsights()">AI Select Insights</button> | |
<div class="grouped-insights-close" onclick="closeGroupedInsights()">×</div> | |
</div> | |
`; | |
// Create content area | |
const content = document.createElement('div'); | |
content.className = 'grouped-insights-content'; | |
// Create score groups | |
for (let score = 5; score >= 0; score--) { | |
const insights = insightsByScore[score].insights; | |
const sources = insightsByScore[score].sources; | |
if (insights.length === 0) continue; | |
const scoreGroup = document.createElement('div'); | |
scoreGroup.className = 'score-group'; | |
scoreGroup.dataset.score = score; | |
// Create score header | |
const scoreHeader = document.createElement('div'); | |
scoreHeader.className = 'score-group-header'; | |
let scoreColor = ''; | |
if (score >= 0 && score <= 5) { | |
const redComponent = Math.floor((score / 5) * 255); | |
const greenComponent = Math.floor(((5 - score) / 5) * 255); | |
scoreColor = `background-color: rgb(${redComponent}, ${greenComponent}, 0); color: white;`; | |
} | |
scoreHeader.innerHTML = ` | |
<div> | |
<span style="display: inline-block; width: 25px; height: 25px; text-align: center; border-radius: 50%; ${scoreColor} font-weight: bold;">${score}</span> | |
<span style="margin-left: 8px;">Score ${score} Insights (${insights.length})</span> | |
</div> | |
<div class="score-group-actions"> | |
<button class="insights-button select-score-all-btn" onclick="selectAllInScore(${score})">Select All</button> | |
<button class="insights-button clear-score-all-btn" onclick="clearAllInScore(${score})">Clear All</button> | |
</div> | |
`; | |
// Create insights list | |
const insightsList = document.createElement('div'); | |
insightsList.className = 'score-insights-list'; | |
// Add each insight | |
insights.forEach((insight, index) => { | |
const source = sources[index]; | |
const tagElement = document.createElement('div'); | |
tagElement.className = `grouped-insight-tag${source.selected ? ' selected' : ''}`; | |
tagElement.dataset.score = score; | |
tagElement.dataset.index = index; | |
tagElement.dataset.source = source.url; | |
// Create insight text with source link | |
tagElement.innerHTML = ` | |
${insight} | |
<a href="${source.url}" target="_blank" class="insight-source" title="${source.title}">${score}</a> | |
`; | |
// Add click event to toggle selection | |
tagElement.addEventListener('click', function(e) { | |
// Prevent clicking on the source link from toggling selection | |
if (e.target.classList.contains('insight-source')) return; | |
this.classList.toggle('selected'); | |
updateGroupedInsightSelections(); | |
}); | |
insightsList.appendChild(tagElement); | |
}); | |
// Add header and list to score group | |
scoreGroup.appendChild(scoreHeader); | |
scoreGroup.appendChild(insightsList); | |
// Add score group to content | |
content.appendChild(scoreGroup); | |
} | |
// Clear the container and add new content | |
container.innerHTML = ''; | |
container.appendChild(header); | |
container.appendChild(content); | |
// Show the container | |
container.style.display = 'block'; | |
// Collapse all accordions | |
const accordions = document.querySelectorAll('.accordion-section'); | |
accordions.forEach(accordion => { | |
const header = accordion.querySelector('.accordion-header'); | |
const body = accordion.querySelector('.accordion-body'); | |
if (header && body && !header.classList.contains('collapsed')) { | |
header.classList.add('collapsed'); | |
body.classList.add('collapsed'); | |
// Update the toggle icon | |
const toggleIcon = header.querySelector('.toggle-icon'); | |
if (toggleIcon) toggleIcon.textContent = '▶'; | |
} | |
}); | |
// Scroll to the insights container | |
container.scrollIntoView({ behavior: 'smooth' }); | |
// Remove the pulse animation after a few seconds | |
setTimeout(() => { | |
const aiSelectBtn = document.getElementById('aiSelectButton'); | |
if (aiSelectBtn) { | |
aiSelectBtn.classList.remove('pulse'); | |
} | |
}, 5000); | |
} | |
// Close the grouped insights view | |
function closeGroupedInsights() { | |
const container = document.getElementById('groupedInsightsContainer'); | |
if (container) { | |
container.style.display = 'none'; | |
} | |
} | |
// Select all insights for a specific score | |
function selectAllInScore(score) { | |
const container = document.getElementById('groupedInsightsContainer'); | |
const tags = container.querySelectorAll(`.grouped-insight-tag[data-score="${score}"]`); | |
tags.forEach(tag => { | |
tag.classList.add('selected'); | |
}); | |
updateGroupedInsightSelections(); | |
} | |
// Clear all insights for a specific score | |
function clearAllInScore(score) { | |
const container = document.getElementById('groupedInsightsContainer'); | |
const tags = container.querySelectorAll(`.grouped-insight-tag[data-score="${score}"]`); | |
tags.forEach(tag => { | |
tag.classList.remove('selected'); | |
}); | |
updateGroupedInsightSelections(); | |
} | |
// Update the original document rows | |
function updateGroupedInsightSelections() { | |
// Get all selected insights from the grouped view | |
const container = document.getElementById('groupedInsightsContainer'); | |
const selectedTags = container.querySelectorAll('.grouped-insight-tag.selected'); | |
// Create a mapping of URL to selected insights | |
const selectionsBySource = {}; | |
// Process each selected tag | |
selectedTags.forEach(tag => { | |
const insight = tag.textContent.trim().replace(/\d+$/, '').trim(); // Remove the score number at the end | |
const sourceUrl = tag.dataset.source; | |
if (!selectionsBySource[sourceUrl]) { | |
selectionsBySource[sourceUrl] = []; | |
} | |
selectionsBySource[sourceUrl].push(insight); | |
}); | |
// Now update the original document rows | |
const queryItems = document.querySelectorAll('.query-item'); | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
const urlLink = row.querySelector('.url-link'); | |
if (!urlLink) return; | |
const sourceUrl = urlLink.href; | |
// If we have selections for this source | |
if (selectionsBySource[sourceUrl]) { | |
// Get all insights for this row | |
try { | |
// Combine selected and unselected to get all insights | |
const currentSelected = JSON.parse(row.dataset.selectedInsights || '[]'); | |
const currentUnselected = JSON.parse(row.dataset.unselectedInsights || '[]'); | |
const allInsights = [...currentSelected, ...currentUnselected]; | |
// New selected and unselected based on grouped view | |
const newSelected = []; | |
const newUnselected = []; | |
// Process each insight | |
allInsights.forEach(insight => { | |
if (selectionsBySource[sourceUrl].includes(insight)) { | |
newSelected.push(insight); | |
} else { | |
newUnselected.push(insight); | |
} | |
}); | |
// Update the dataset | |
row.dataset.selectedInsights = JSON.stringify(newSelected); | |
row.dataset.unselectedInsights = JSON.stringify(newUnselected); | |
} catch (e) { | |
console.error('Error updating insights selections:', e); | |
} | |
} | |
}); | |
}); | |
// Update enhance button state | |
checkSelectedInsights(); | |
} | |
// Use AI to select the most relevant insights across all score groups | |
function aiSelectInsights() { | |
const patentBackground = document.getElementById('userInput').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
// Change button appearance to show it's working | |
const aiSelectBtn = document.getElementById('aiSelectButton'); | |
if (aiSelectBtn) { | |
aiSelectBtn.disabled = true; | |
aiSelectBtn.textContent = 'AI Selecting...'; | |
aiSelectBtn.style.opacity = '0.7'; | |
} | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'AI selecting the most relevant insights...'; | |
} | |
// Get all insights from all score groups | |
const container = document.getElementById('groupedInsightsContainer'); | |
const insightTags = container.querySelectorAll('.grouped-insight-tag'); | |
const insights = Array.from(insightTags).map(tag => { | |
return tag.textContent.trim().replace(/\d+$/, '').trim(); // Remove the score number | |
}); | |
if (insights.length === 0) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert('No insights found'); | |
return; | |
} | |
// Send request to backend to get AI selection | |
fetch('/ai-select-insights', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'insights': insights | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Reset button appearance | |
if (aiSelectBtn) { | |
aiSelectBtn.disabled = false; | |
aiSelectBtn.textContent = 'AI Select Insights'; | |
aiSelectBtn.style.opacity = '1'; | |
} | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
if (data.error) { | |
alert(`Error: ${data.error}`); | |
return; | |
} | |
if (data.selected_insights && data.selected_insights.length > 0) { | |
// First clear all selections | |
insightTags.forEach(tag => { | |
tag.classList.remove('selected'); | |
}); | |
// Then select the recommended insights | |
insightTags.forEach(tag => { | |
const insightText = tag.textContent.trim().replace(/\d+$/, '').trim(); | |
if (data.selected_insights.includes(insightText)) { | |
tag.classList.add('selected'); | |
} | |
}); | |
// Update the original document rows | |
updateGroupedInsightSelections(); | |
// Remove alert notification - user can see the selected insights visually | |
// alert(`AI selected ${data.selected_insights.length} insights from ${insights.length} available insights`); | |
} else { | |
alert('AI could not identify any relevant insights to select'); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
// Reset button appearance | |
if (aiSelectBtn) { | |
aiSelectBtn.disabled = false; | |
aiSelectBtn.textContent = 'AI Select Insights'; | |
aiSelectBtn.style.opacity = '1'; | |
} | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error using AI to select insights: ${error.message}`); | |
}); | |
} | |
// Check if any documents have been analyzed to enable/disable the Group Insights button | |
function checkGroupInsightsButton() { | |
const queryItems = document.querySelectorAll('.query-item'); | |
let hasInsights = false; | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
if (row.dataset.selectedInsights || row.dataset.unselectedInsights) { | |
try { | |
const selected = JSON.parse(row.dataset.selectedInsights || '[]'); | |
const unselected = JSON.parse(row.dataset.unselectedInsights || '[]'); | |
if (selected.length > 0 || unselected.length > 0) { | |
hasInsights = true; | |
} | |
} catch (e) {} | |
} | |
}); | |
}); | |
const groupInsightsButton = document.getElementById('groupInsightsByScoreButton'); | |
if (hasInsights) { | |
groupInsightsButton.classList.remove('disabled'); | |
groupInsightsButton.disabled = false; | |
} else { | |
groupInsightsButton.classList.add('disabled'); | |
groupInsightsButton.disabled = true; | |
} | |
} | |
function generateSearchQueries() { | |
const userInput = document.getElementById('userInput').value.trim(); | |
if (!userInput) { | |
alert('Please enter a technical problem description'); | |
return; | |
} | |
// Show loading indicator | |
document.getElementById('loadingIndicator').style.display = 'block'; | |
// Collapse all existing query sections | |
const existingAccordions = document.querySelectorAll('.accordion-section'); | |
existingAccordions.forEach(accordion => { | |
const header = accordion.querySelector('.accordion-header'); | |
const body = accordion.querySelector('.accordion-body'); | |
if (header && body && !header.classList.contains('collapsed')) { | |
header.classList.add('collapsed'); | |
body.classList.add('collapsed'); | |
} | |
}); | |
// Send message to Flask backend | |
fetch('/chat', { | |
method: 'POST', | |
body: new URLSearchParams({ 'message': userInput }), | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
document.getElementById('loadingIndicator').style.display = 'none'; | |
try { | |
// Parse the JSON string from the LLM response | |
let jsonData; | |
// First check if data.reply is already an object | |
if (typeof data.reply === 'object') { | |
jsonData = data.reply; | |
} else { | |
// Try to extract JSON if it's a string possibly with markdown code blocks | |
const jsonString = data.reply.replace(/```json|```/g, '').trim(); | |
jsonData = JSON.parse(jsonString); | |
} | |
// Create a new accordion section for these search results | |
const timestamp = new Date().toLocaleString(); | |
const accordionSection = createAccordionSection(timestamp); | |
// Add each query from the response to this accordion | |
Object.keys(jsonData).forEach(key => { | |
addQueryField(jsonData[key], accordionSection.querySelector('.accordion-content')); | |
}); | |
// Add the accordion to the container | |
const queriesContainer = document.getElementById('queriesContainer'); | |
queriesContainer.insertBefore(accordionSection, queriesContainer.firstChild); | |
// Show results container | |
document.getElementById('resultsContainer').style.display = 'block'; | |
} catch (error) { | |
console.error('Error parsing JSON:', error); | |
alert('There was an error processing the response. Please try again.'); | |
} | |
}) | |
.catch(error => { | |
document.getElementById('loadingIndicator').style.display = 'none'; | |
console.error('Error:', error); | |
alert('There was an error communicating with the server. Please try again.'); | |
}); | |
} | |
</script> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
const form = document.querySelector('form'); | |
const userInput = document.getElementById('userInput'); | |
const loadingIndicator = document.getElementById('loadingIndicator'); | |
const keyIssuesContainer = document.getElementById('keyIssuesContainer'); | |
const keyIssuesList = document.getElementById('keyIssuesList'); | |
const insightCommentContainer = document.getElementById('insightCommentContainer'); | |
// Store selected key issues | |
let selectedKeyIssues = []; | |
// Handle form submission | |
form.addEventListener('submit', async (e) => { | |
e.preventDefault(); | |
const userQuery = userInput.value.trim(); | |
if (!userQuery) { | |
alert('Please enter a problem description.'); | |
return; | |
} | |
// Show loading indicator | |
loadingIndicator.style.display = 'block'; | |
keyIssuesContainer.style.display = 'none'; | |
keyIssuesList.innerHTML = ''; | |
try { | |
// Call the generate-key-issues endpoint | |
const response = await fetch('/generate-key-issues', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ query: userQuery }), | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP error! Status: ${response.status}`); | |
} | |
const data = await response.json(); | |
if (data.error) { | |
alert(`Error: ${data.error}`); | |
return; | |
} | |
// Display key issues | |
displayKeyIssues(data.key_issues); | |
} catch (error) { | |
console.error('Error generating key issues:', error); | |
alert(`Error generating key issues: ${error.message}`); | |
} finally { | |
// Hide loading indicator | |
loadingIndicator.style.display = 'none'; | |
} | |
}); | |
// Function to display key issues | |
function displayKeyIssues(keyIssues) { | |
if (!keyIssues || keyIssues.length === 0) { | |
alert('No key issues found.'); | |
return; | |
} | |
// Reset selected issues | |
selectedKeyIssues = []; | |
// Clear previous issues | |
keyIssuesList.innerHTML = ''; | |
// Create and append key issue cards | |
keyIssues.forEach(issue => { | |
const issueCard = document.createElement('div'); | |
issueCard.className = 'key-issue-card'; | |
issueCard.dataset.issueId = issue.id; | |
// Format the content with title, description, challenges and impact | |
let cardContent = ` | |
<div class="key-issue-title">${issue.title}</div> | |
<div class="key-issue-description">${issue.description}</div> | |
<ul class="key-issue-challenges"> | |
`; | |
// Add each challenge as a list item | |
issue.challenges.forEach(challenge => { | |
cardContent += `<li>${challenge}</li>`; | |
}); | |
cardContent += ` | |
</ul> | |
<div class="key-issue-impact">Impact: ${issue.potential_impact}</div> | |
`; | |
issueCard.innerHTML = cardContent; | |
// Add click handler to toggle selection | |
issueCard.addEventListener('click', () => { | |
issueCard.classList.toggle('selected'); | |
if (issueCard.classList.contains('selected')) { | |
selectedKeyIssues.push(issue); | |
} else { | |
selectedKeyIssues = selectedKeyIssues.filter(item => item.id !== issue.id); | |
} | |
// Update the comment area based on selections | |
updateCommentArea(); | |
}); | |
keyIssuesList.appendChild(issueCard); | |
}); | |
// Show the key issues container | |
keyIssuesContainer.style.display = 'block'; | |
} | |
// Function to update the comment area when key issues are selected | |
function updateCommentArea() { | |
if (selectedKeyIssues.length > 0) { | |
let commentHTML = ` | |
<h4>Selected Key Issues</h4> | |
<textarea class="insight-comment-textarea" placeholder="Add your thoughts about these key issues..."></textarea> | |
`; | |
insightCommentContainer.innerHTML = commentHTML; | |
} else { | |
insightCommentContainer.innerHTML = ''; | |
} | |
} | |
}); | |
</script> | |
<script> | |
// Add event listeners for floating buttons | |
document.addEventListener('DOMContentLoaded', function() { | |
// Initialize the comment area | |
initializeCommentArea(); | |
// Check for selected insights periodically to enable/disable enhance button | |
setInterval(checkSelectedInsights, 1000); | |
// Check for analyzed documents to enable/disable Extract All Insights button | |
setInterval(checkAnalyzedDocuments, 1000); | |
// Check for insights to enable/disable Group Insights by Score button | |
setInterval(checkGroupInsightsButton, 1000); | |
// Check if search has been performed and update button states | |
setInterval(checkSearchPerformed, 1000); | |
// Analyze all button | |
document.getElementById('analyzeAllButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
analyzeAllPapers(); | |
} | |
}); | |
// Remove failed button | |
document.getElementById('removeFailedButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
removeFailedAnalyses(); | |
} | |
}); | |
// Export to Excel button | |
document.getElementById('exportExcelButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
exportToExcel(); | |
} | |
}); | |
// Generate Search Queries floating button | |
document.getElementById('generateQueriesButton').addEventListener('click', function() { | |
// Call the search queries generation function | |
generateSearchQueries(); | |
}); | |
// Initialize the collapsible ribbon | |
initRibbonAccordion(); | |
// Set up ribbon button events | |
setupRibbonButtons(); | |
// Auto Run All button | |
document.getElementById('autoRunAllButton').addEventListener('click', function() { | |
autoRunAllProcess(); | |
}); | |
}); | |
// Function to check if any search has been performed | |
function hasSearchResults() { | |
const resultsContainer = document.getElementById('resultsContainer'); | |
const hasResults = resultsContainer && resultsContainer.style.display !== 'none'; | |
const hasTables = document.querySelectorAll('.results-table[style*="display: table"]').length > 0; | |
return hasResults && hasTables; | |
} | |
// Function to check if search has been performed and update all button states | |
function checkSearchPerformed() { | |
const searchPerformed = hasSearchResults(); | |
// Get all floating buttons | |
const analyzeAllButton = document.getElementById('analyzeAllButton'); | |
const removeFailedButton = document.getElementById('removeFailedButton'); | |
const extractAllInsightsButton = document.getElementById('extractAllInsightsButton'); | |
const groupInsightsButton = document.getElementById('groupInsightsByScoreButton'); | |
const enhanceProblemButton = document.getElementById('enhanceProblemButton'); | |
const exportExcelButton = document.getElementById('exportExcelButton'); | |
// Set disabled state for buttons that should be disabled when no search is performed | |
if (!searchPerformed) { | |
analyzeAllButton.classList.add('disabled'); | |
analyzeAllButton.disabled = true; | |
removeFailedButton.classList.add('disabled'); | |
removeFailedButton.disabled = true; | |
exportExcelButton.classList.add('disabled'); | |
exportExcelButton.disabled = true; | |
// The other buttons (extract, group, enhance) should already be disabled by their respective check functions | |
} else { | |
analyzeAllButton.classList.remove('disabled'); | |
analyzeAllButton.disabled = false; | |
removeFailedButton.classList.remove('disabled'); | |
removeFailedButton.disabled = false; | |
exportExcelButton.classList.remove('disabled'); | |
exportExcelButton.disabled = false; | |
// The other buttons will be enabled/disabled by their respective check functions | |
} | |
} | |
// Initialize the ribbon accordion | |
function initRibbonAccordion() { | |
const ribbon = document.querySelector('.ribbon-accordion'); | |
const ribbonHeader = ribbon.querySelector('.ribbon-header'); | |
ribbonHeader.addEventListener('click', function() { | |
ribbon.classList.toggle('collapsed'); | |
}); | |
} | |
// Set up ribbon button events (mirror the functionality of the floating buttons) | |
function setupRibbonButtons() { | |
// Generate Queries | |
document.getElementById('ribbonGenerateQueriesButton').addEventListener('click', function() { | |
generateSearchQueries(); | |
}); | |
// Analyze All | |
document.getElementById('ribbonAnalyzeAllButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
analyzeAllPapers(); | |
} | |
}); | |
// Remove Failed | |
document.getElementById('ribbonRemoveFailedButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
removeFailedAnalyses(); | |
} | |
}); | |
// Extract All Insights | |
document.getElementById('ribbonExtractAllInsightsButton').addEventListener('click', function() { | |
extractAllInsights(); | |
}); | |
// Group Insights | |
document.getElementById('ribbonGroupInsightsButton').addEventListener('click', function() { | |
groupInsightsByScore(); | |
}); | |
// Enhance Problem | |
document.getElementById('ribbonEnhanceProblemButton').addEventListener('click', function() { | |
enhanceProblem(); | |
}); | |
// Export to Excel | |
document.getElementById('ribbonExportExcelButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
exportToExcel(); | |
} | |
}); | |
} | |
// Auto Run All Process - This automates the entire workflow | |
async function autoRunAllProcess() { | |
// Check if there's a problem description | |
const userInput = document.getElementById('userInput').value.trim(); | |
if (!userInput) { | |
alert('Please enter a technical problem description first'); | |
return; | |
} | |
// Show the ribbon fully | |
document.querySelector('.ribbon-accordion').classList.remove('collapsed'); | |
// Show progress bar and step indicator | |
const progressBarContainer = document.querySelector('.progress-bar-container'); | |
const progressBar = document.querySelector('.progress-bar'); | |
const progressStep = document.querySelector('.progress-step'); | |
progressBarContainer.style.display = 'block'; | |
progressStep.style.display = 'block'; | |
progressBar.style.width = '0%'; | |
// Disable the auto run button | |
const autoRunBtn = document.getElementById('autoRunAllButton'); | |
autoRunBtn.disabled = true; | |
autoRunBtn.style.opacity = '0.7'; | |
autoRunBtn.textContent = 'Running...'; | |
try { | |
// Step 1: Generate search queries | |
progressStep.textContent = '1/7: Generating search queries...'; | |
progressBar.style.width = '10%'; | |
await new Promise(resolve => { | |
// Call generate queries function | |
generateSearchQueries(); | |
// Wait for results to appear | |
const checkInterval = setInterval(() => { | |
if (document.querySelector('.accordion-section')) { | |
clearInterval(checkInterval); | |
resolve(); | |
} | |
}, 500); | |
// Timeout after 30 seconds in case of error | |
setTimeout(() => { | |
clearInterval(checkInterval); | |
resolve(); | |
}, 30000); | |
}); | |
// Step 2: Perform searches for each query | |
progressStep.textContent = '2/7: Performing searches for each query...'; | |
progressBar.style.width = '25%'; | |
await new Promise(resolve => { | |
const queryItems = document.querySelectorAll('.query-item'); | |
let totalQueries = 0; | |
let completedQueries = 0; | |
if (queryItems.length === 0) { | |
resolve(); | |
return; | |
} | |
queryItems.forEach((queryItem, index) => { | |
const queryField = queryItem.querySelector('.query-field'); | |
if (queryField && queryField.value.trim()) { | |
totalQueries++; | |
// Slight delay between searches to prevent overloading | |
setTimeout(() => { | |
const searchBtn = queryItem.querySelector('.search-button'); | |
if (searchBtn) { | |
searchBtn.click(); | |
// Wait for search to complete | |
const checkInterval = setInterval(() => { | |
const loadingElement = queryItem.querySelector('.search-loading'); | |
if (loadingElement && loadingElement.style.display === 'none') { | |
clearInterval(checkInterval); | |
completedQueries++; | |
if (completedQueries >= totalQueries) { | |
resolve(); | |
} | |
} | |
}, 500); | |
// Timeout after 30 seconds per query | |
setTimeout(() => { | |
clearInterval(checkInterval); | |
completedQueries++; | |
if (completedQueries >= totalQueries) { | |
resolve(); | |
} | |
}, 30000); | |
} | |
}, index * 2000); // Stagger searches by 2 seconds | |
} | |
}); | |
// If no valid queries found | |
if (totalQueries === 0) { | |
resolve(); | |
} | |
}); | |
// Step 3: Analyze all retrieved documents | |
progressStep.textContent = '3/7: Analyzing all retrieved documents...'; | |
progressBar.style.width = '40%'; | |
await new Promise(resolve => { | |
analyzeAllPapers(); | |
// Wait for analyses to complete | |
const checkInterval = setInterval(() => { | |
// Check if the global loading overlay is gone | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay && loadingOverlay.style.display === 'none') { | |
clearInterval(checkInterval); | |
resolve(); | |
} | |
}, 500); | |
// Timeout after 2 minutes | |
setTimeout(() => { | |
clearInterval(checkInterval); | |
resolve(); | |
}, 120000); | |
}); | |
// Step 4: Remove failed analyses | |
progressStep.textContent = '4/7: Removing failed analyses...'; | |
progressBar.style.width = '50%'; | |
removeFailedAnalyses(); | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
// Step 5: Extract insights from all documents | |
progressStep.textContent = '5/7: Extracting insights from all documents...'; | |
progressBar.style.width = '65%'; | |
await new Promise(resolve => { | |
extractAllInsights(); | |
// Wait for extractions to complete | |
const checkInterval = setInterval(() => { | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay && loadingOverlay.style.display === 'none') { | |
clearInterval(checkInterval); | |
resolve(); | |
} | |
}, 500); | |
// Timeout after 3 minutes | |
setTimeout(() => { | |
clearInterval(checkInterval); | |
resolve(); | |
}, 180000); | |
}); | |
// Step 6: Group insights by score | |
progressStep.textContent = '6/7: Grouping insights by score...'; | |
progressBar.style.width = '80%'; | |
groupInsightsByScore(); | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
// Step 7: AI suggest insights | |
progressStep.textContent = '7/7: Using AI to suggest the best insights...'; | |
progressBar.style.width = '90%'; | |
await new Promise(resolve => { | |
// Find and click the AI Select Insights button | |
const aiSelectBtn = document.getElementById('aiSelectButton'); | |
if (aiSelectBtn) { | |
aiSelectBtn.click(); | |
// Wait for AI selection to complete | |
const checkInterval = setInterval(() => { | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay && loadingOverlay.style.display === 'none') { | |
clearInterval(checkInterval); | |
resolve(); | |
} | |
}, 500); | |
// Timeout after 1 minute | |
setTimeout(() => { | |
clearInterval(checkInterval); | |
resolve(); | |
}, 60000); | |
} else { | |
resolve(); | |
} | |
}); | |
// Step 8: Enhance problem using selected insights | |
progressStep.textContent = 'Enhancing problem with selected insights...'; | |
progressBar.style.width = '100%'; | |
enhanceProblem(); | |
// Process complete | |
setTimeout(() => { | |
progressStep.textContent = 'All processes completed successfully!'; | |
// Re-enable the auto run button | |
autoRunBtn.disabled = false; | |
autoRunBtn.style.opacity = '1'; | |
autoRunBtn.textContent = 'Auto Run All Process'; | |
// Hide progress bar after 5 seconds | |
setTimeout(() => { | |
if (!autoRunBtn.disabled) { // Only hide if not running again | |
progressBarContainer.style.display = 'none'; | |
progressStep.style.display = 'none'; | |
} | |
}, 5000); | |
}, 1000); | |
} catch (error) { | |
console.error('Error in auto run process:', error); | |
progressStep.textContent = 'Process interrupted due to an error.'; | |
progressBar.style.width = '100%'; | |
progressBar.style.backgroundColor = '#ef4444'; | |
// Re-enable the auto run button | |
autoRunBtn.disabled = false; | |
autoRunBtn.style.opacity = '1'; | |
autoRunBtn.textContent = 'Auto Run All Process'; | |
} | |
} | |
</script> | |
</head> | |
<body> | |
<div class="container"> | |
<header class="header"> | |
<div class="logo"> | |
<div class="logo-icon">P</div> | |
<h1>Patentability</h1> | |
</div> | |
<p>Enter a detailed description of your technical problem to generate search queries for finding relevant research papers.</p> | |
</header> | |
<section class="card"> | |
<form id="queryForm" onsubmit="generateQueries(event)"> | |
<div class="form-group"> | |
<label for="userInput">Technical Problem Description:</label> | |
<div class="problem-history"> | |
<div id="problemHistoryNav" class="problem-history-nav" style="display: none;"> | |
<div class="history-arrow history-prev disabled" onclick="navigateProblemHistory('prev')">←</div> | |
<div class="history-arrow history-next disabled" onclick="navigateProblemHistory('next')">→</div> | |
<div class="history-status"></div> | |
</div> | |
<textarea id="userInput" placeholder="Describe your technical problem in detail..." required></textarea> | |
</div> | |
</div> | |
<button type="submit" class="btn btn-primary">Generate Key Issues</button> | |
</form> | |
<div id="loadingIndicator"> | |
<div class="loading-spinner"></div> | |
<p>Generating key issues... Please wait.</p> | |
</div> | |
<div id="keyIssuesContainer" class="key-issues-container" style="display: none;"> | |
<h3>Key Technical Issues</h3> | |
<div id="keyIssuesList" class="key-issues-list"> | |
<!-- Key issues will be added here dynamically --> | |
</div> | |
</div> | |
<div id="insightCommentContainer" class="insight-comment-area"></div> | |
<div id="refinedProblemContainer" class="refined-problem-container" style="display: none;"> | |
<div id="refinedProblemTabs" class="refined-problem-tabs"></div> | |
<div id="refinedProblemContent" class="refined-problem-content"></div> | |
</div> | |
</section> | |
<section class="search-options"> | |
<label>Search Options:</label> | |
<div class="checkbox-group"> | |
<div class="checkbox-item"> | |
<input type="checkbox" id="pdfOption" name="searchOptions" value="pdf" checked> | |
<label for="pdfOption">PDF</label> | |
</div> | |
<div class="checkbox-item"> | |
<input type="checkbox" id="patentOption" name="searchOptions" value="patent"> | |
<label for="patentOption">Patent</label> | |
</div> | |
<div class="checkbox-item"> | |
<input type="checkbox" id="webOption" name="searchOptions" value="web"> | |
<label for="webOption">Web</label> | |
</div> | |
</div> | |
</section> | |
<section id="resultsContainer"> | |
<h2>Generated Search Queries</h2> | |
<div id="queriesContainer"> | |
<!-- Query fields will be added here dynamically --> | |
</div> | |
<div class="button-container"> | |
<button type="button" class="btn btn-secondary" onclick="addQueryField()"> | |
<span>Add New Query</span> | |
</button> | |
</div> | |
</section> | |
</div> | |
<!-- Global action buttons (floating) --> | |
<div class="floating-buttons"> | |
<button id="generateQueriesButton" class="btn floating-button" style="background-color: #8e44ad; color: white;" title="Generate Search Queries"> | |
Generate Search Queries | |
</button> | |
<button id="analyzeAllButton" class="btn btn-primary floating-button" title="Analyze All Unanalyzed Papers"> | |
Analyze All | |
</button> | |
<button id="removeFailedButton" class="btn btn-danger floating-button" title="Remove Papers with Failed Analyses"> | |
Remove Failed | |
</button> | |
<button id="extractAllInsightsButton" class="btn btn-info floating-button disabled" title="Extract Insights for All Analyzed Documents" onclick="extractAllInsights()" disabled> | |
Extract All Insights | |
</button> | |
<button id="groupInsightsByScoreButton" class="btn btn-group-insights floating-button disabled" title="Group All Insights by Score" onclick="groupInsightsByScore()" disabled> | |
Group Insights | |
</button> | |
<button id="enhanceProblemButton" class="btn enhance-problem-button floating-button disabled" title="Enhance Problem using Selected Insights" onclick="enhanceProblem()" disabled> | |
Enhance Problem | |
</button> | |
<button id="exportExcelButton" class="btn btn-success floating-button" title="Export All Data to Excel"> | |
Export to Excel | |
</button> | |
</div> | |
<!-- Global loading overlay --> | |
<div id="globalLoadingOverlay" class="loading-overlay"> | |
<div class="loading-content"> | |
<div class="loading-spinner"></div> | |
<div class="progress-text">Processing...</div> | |
</div> | |
</div> | |
<!-- Collapsible ribbon accordion --> | |
<div class="ribbon-accordion collapsed"> | |
<div class="ribbon-header"> | |
<div class="ribbon-title"> | |
<span class="ribbon-icon">🧰</span> | |
<span>Patentability Tools</span> | |
</div> | |
<div class="ribbon-toggle">▲</div> | |
</div> | |
<div class="ribbon-content"> | |
<button id="autoRunAllButton" class="auto-run-btn">Auto Run All Process</button> | |
<div class="progress-bar-container"> | |
<div class="progress-bar"></div> | |
</div> | |
<div class="progress-step">Waiting to start...</div> | |
<button id="ribbonGenerateQueriesButton" class="btn floating-button" style="background-color: #8e44ad; color: white;" title="Generate Search Queries"> | |
Generate Search Queries | |
</button> | |
<button id="ribbonAnalyzeAllButton" class="btn btn-primary floating-button" title="Analyze All Unanalyzed Papers"> | |
Analyze All | |
</button> | |
<button id="ribbonRemoveFailedButton" class="btn btn-danger floating-button" title="Remove Papers with Failed Analyses"> | |
Remove Failed | |
</button> | |
<button id="ribbonExtractAllInsightsButton" class="btn btn-info floating-button" title="Extract Insights for All Analyzed Documents"> | |
Extract All Insights | |
</button> | |
<button id="ribbonGroupInsightsButton" class="btn btn-group-insights floating-button" title="Group All Insights by Score"> | |
Group Insights | |
</button> | |
<button id="ribbonEnhanceProblemButton" class="btn enhance-problem-button floating-button" title="Enhance Problem using Selected Insights"> | |
Enhance Problem | |
</button> | |
<button id="ribbonExportExcelButton" class="btn btn-success floating-button" title="Export All Data to Excel"> | |
Export to Excel | |
</button> | |
</div> | |
</div> | |
<script> | |
// Add event listeners for floating buttons | |
document.addEventListener('DOMContentLoaded', function() { | |
// Initialize the comment area | |
initializeCommentArea(); | |
// Check for selected insights periodically to enable/disable enhance button | |
setInterval(checkSelectedInsights, 1000); | |
// Check for analyzed documents to enable/disable Extract All Insights button | |
setInterval(checkAnalyzedDocuments, 1000); | |
// Check for insights to enable/disable Group Insights by Score button | |
setInterval(checkGroupInsightsButton, 1000); | |
// Check if search has been performed and update button states | |
setInterval(checkSearchPerformed, 1000); | |
// Analyze all button | |
document.getElementById('analyzeAllButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
analyzeAllPapers(); | |
} | |
}); | |
// Remove failed button | |
document.getElementById('removeFailedButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
removeFailedAnalyses(); | |
} | |
}); | |
// Export to Excel button | |
document.getElementById('exportExcelButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
exportToExcel(); | |
} | |
}); | |
// Generate Search Queries floating button | |
document.getElementById('generateQueriesButton').addEventListener('click', function() { | |
// Call the search queries generation function | |
generateSearchQueries(); | |
}); | |
}); | |
// Function to check if any search has been performed | |
function hasSearchResults() { | |
const resultsContainer = document.getElementById('resultsContainer'); | |
const hasResults = resultsContainer && resultsContainer.style.display !== 'none'; | |
const hasTables = document.querySelectorAll('.results-table[style*="display: table"]').length > 0; | |
return hasResults && hasTables; | |
} | |
// Function to check if search has been performed and update all button states | |
function checkSearchPerformed() { | |
const searchPerformed = hasSearchResults(); | |
// Get all floating buttons | |
const analyzeAllButton = document.getElementById('analyzeAllButton'); | |
const removeFailedButton = document.getElementById('removeFailedButton'); | |
const extractAllInsightsButton = document.getElementById('extractAllInsightsButton'); | |
const groupInsightsButton = document.getElementById('groupInsightsByScoreButton'); | |
const enhanceProblemButton = document.getElementById('enhanceProblemButton'); | |
const exportExcelButton = document.getElementById('exportExcelButton'); | |
// Set disabled state for buttons that should be disabled when no search is performed | |
if (!searchPerformed) { | |
analyzeAllButton.classList.add('disabled'); | |
analyzeAllButton.disabled = true; | |
removeFailedButton.classList.add('disabled'); | |
removeFailedButton.disabled = true; | |
exportExcelButton.classList.add('disabled'); | |
exportExcelButton.disabled = true; | |
// The other buttons (extract, group, enhance) should already be disabled by their respective check functions | |
} else { | |
analyzeAllButton.classList.remove('disabled'); | |
analyzeAllButton.disabled = false; | |
removeFailedButton.classList.remove('disabled'); | |
removeFailedButton.disabled = false; | |
exportExcelButton.classList.remove('disabled'); | |
exportExcelButton.disabled = false; | |
// The other buttons will be enabled/disabled by their respective check functions | |
} | |
} | |
</script> | |
</body> | |
</html> |