Spaces:
Sleeping
Sleeping
// 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'); | |
// Add to selected key issues if not already there | |
if (!selectedKeyIssues.some(item => item.id === issue.id)) { | |
selectedKeyIssues.push(issue); | |
} | |
} else { | |
issueCard.classList.remove('selected'); | |
// Remove from selected key issues | |
selectedKeyIssues = selectedKeyIssues.filter(item => item.id !== issue.id); | |
} | |
// Update the comment area based on selections | |
updateCommentArea(); | |
}); | |
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; | |
// No direct click handler on issue cards - they only get selected through challenge selection | |
// Initialize the dataset.selectedChallenges array | |
issueCard.dataset.selectedChallenges = JSON.stringify([]); | |
// 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); | |
// Create API submit button (floating style) | |
const apiSubmitButton = document.createElement('button'); | |
apiSubmitButton.id = 'generatePriorArtButton'; | |
apiSubmitButton.className = 'btn btn-primary floating-button'; | |
apiSubmitButton.textContent = 'Generate Prior Art'; | |
apiSubmitButton.style.backgroundColor = '#10b981'; // Green color to differentiate | |
apiSubmitButton.style.color = 'white'; | |
apiSubmitButton.addEventListener('click', submitSelectedKeyIssuesToAPI); | |
// Add to floating buttons container | |
document.querySelector('.floating-buttons').appendChild(apiSubmitButton); | |
// Show the results container | |
document.getElementById('resultsContainer').style.display = 'block'; | |
} | |
// Function to submit selected key issues to the API | |
function submitSelectedKeyIssuesToAPI() { | |
const userInput = document.getElementById('userInput').value.trim(); | |
if (!userInput) { | |
alert('Please provide a technical problem description'); | |
return; | |
} | |
// Find all key issue cards with selected challenges | |
const issueCards = document.querySelectorAll('.key-issue-card'); | |
const keyIssuesData = []; | |
issueCards.forEach(card => { | |
const selectedChallenges = JSON.parse(card.dataset.selectedChallenges || '[]'); | |
// Only include issues with at least one selected challenge | |
if (selectedChallenges.length > 0) { | |
const issueDescription = card.querySelector('.key-issue-description').textContent; | |
keyIssuesData.push({ | |
"description": issueDescription, | |
"challenges": selectedChallenges | |
}); | |
} | |
}); | |
// If no key issues with selected challenges, show message | |
if (keyIssuesData.length === 0) { | |
alert('Please select at least one challenge in a key issue'); | |
return; | |
} | |
// Create or update API response container with loading message - using refined problem container style | |
let apiResponseContainer = document.getElementById('apiResponseContainer'); | |
if (!apiResponseContainer) { | |
apiResponseContainer = document.createElement('div'); | |
apiResponseContainer.id = 'apiResponseContainer'; | |
apiResponseContainer.className = 'refined-problem-container'; | |
document.getElementById('resultsContainer').appendChild(apiResponseContainer); | |
} | |
// Set loading message | |
apiResponseContainer.innerHTML = ` | |
<div class="refined-problem-tabs"> | |
<div class="refined-problem-tab active">Processing</div> | |
</div> | |
<div class="refined-problem-content"> | |
<div class="refined-problem active" style="text-align: center;"> | |
<div class="loading-spinner" style="margin: 20px auto;"></div> | |
<p>Generating prior art based on selected key issues and challenges. This may take a moment...</p> | |
</div> | |
</div> | |
`; | |
// Show the container and scroll to it | |
apiResponseContainer.style.display = 'block'; | |
apiResponseContainer.scrollIntoView({ behavior: 'smooth' }); | |
// Prepare the payload | |
const payload = { | |
"keys_issues": keyIssuesData, | |
"technical_topic": userInput | |
}; | |
console.log('Sending data to API:', JSON.stringify(payload, null, 2)); | |
// Send data to API | |
fetch('/generate-prior-art', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(payload) | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`HTTP error! Status: ${response.status}`); | |
} | |
return response.json(); | |
}) | |
.then(data => { | |
console.log('API Response:', data); | |
// For testing, we can use a mock response based on the example format | |
// Remove this in production | |
if (!data || (Array.isArray(data) && data.length === 0)) { | |
console.log('Using mock data for testing'); | |
data = [ | |
{ | |
"problem_description": "I am working on managing conflicting instructions for network behavior in 5G systems. The core problem is how to ensure that requests coming dynamically from applications serving critical functions are reliably given higher priority and override the static network settings stored in a user's plan. This requires a solution that addresses the security risks associated with allowing applications to dictate network treatment and ensures this rule is enforced uniformly across the central network components.", | |
"problematic": "How can 5G core network functions securely and reliably enforce dynamic network treatment instructions from critical function applications, ensuring they consistently override conflicting static policy configurations derived from user plans, while uniformly mitigating the security risks associated with application-level control over network behavior?", | |
"score": 0.845 | |
}, | |
{ | |
"problem_description": "I am working on enhancing security in 5G networks, specifically related to how network segments are configured based on user profiles. My goal is to address the security risks that arise when requests from external applications conflict with the priority settings defined in a user's profile for essential services, ensuring that unauthorized changes to service priority are securely prevented and handled throughout the core network.", | |
"problematic": "How can security risks arising from external application requests conflicting with user profile-defined priority settings for essential services in 5G networks be securely mitigated, ensuring that unauthorized modifications to service priority are effectively prevented and managed throughout the core network segments configured based on these profiles?", | |
"score": 0.624 | |
}, | |
{ | |
"problem_description": "I am working on managing conflicting instructions for network behavior in 5G systems. The core problem is how to ensure that requests coming dynamically from applications serving critical functions are reliably given higher priority and override the static network settings stored in a user's plan. This requires a solution that addresses the security risks associated with allowing applications to dictate network treatment and ensures this rule is enforced uniformly across the central network components.", | |
"problematic": "How can 5G core network functions securely and reliably enforce dynamic network treatment instructions from critical function applications, ensuring they consistently override conflicting static policy configurations derived from user plans, while uniformly mitigating the security risks associated with application-level control over network behavior?", | |
"score": 0.312 | |
}, | |
{ | |
"problem_description": "I am working on enhancing security in 5G networks, specifically related to how network segments are configured based on user profiles. My goal is to address the security risks that arise when requests from external applications conflict with the priority settings defined in a user's profile for essential services, ensuring that unauthorized changes to service priority are securely prevented and handled throughout the core network.", | |
"problematic": "How can security risks arising from external application requests conflicting with user profile-defined priority settings for essential services in 5G networks be securely mitigated, ensuring that unauthorized modifications to service priority are effectively prevented and managed throughout the core network segments configured based on these profiles?", | |
"score": 0.305 | |
} | |
]; | |
} | |
// Display the API response | |
displayAPIResponse(data); | |
}) | |
.catch(error => { | |
console.error('Error submitting to API:', error); | |
// Display error message in the API response container | |
apiResponseContainer.innerHTML = ` | |
<div class="refined-problem-tabs"> | |
<div class="refined-problem-tab active">Error</div> | |
</div> | |
<div class="refined-problem-content"> | |
<div class="refined-problem active"> | |
<div class="refined-problem-title">Error Generating Prior Art</div> | |
<div class="refined-problem-description" style="color: #ef4444;"> | |
<p>There was an error communicating with the API: ${error.message}</p> | |
<p>Please try again later or contact support.</p> | |
</div> | |
</div> | |
</div> | |
`; | |
}); | |
} | |
// Function to display the API response in a tabbed format | |
function displayAPIResponse(data) { | |
// Get or create container for API response | |
let apiResponseContainer = document.getElementById('apiResponseContainer'); | |
if (!apiResponseContainer) { | |
apiResponseContainer = document.createElement('div'); | |
apiResponseContainer.id = 'apiResponseContainer'; | |
apiResponseContainer.className = 'refined-problem-container'; // Use the same style as refined problems | |
document.getElementById('resultsContainer').appendChild(apiResponseContainer); | |
} | |
// Clear previous content | |
apiResponseContainer.innerHTML = ''; | |
// Create tabs and content containers similar to refined problems | |
const tabs = document.createElement('div'); | |
tabs.className = 'refined-problem-tabs'; | |
apiResponseContainer.appendChild(tabs); | |
const content = document.createElement('div'); | |
content.className = 'refined-problem-content'; | |
apiResponseContainer.appendChild(content); | |
try { | |
// Parse the data if it's a string | |
const problems = Array.isArray(data) ? data : | |
(typeof data === 'string' ? JSON.parse(data) : | |
(data.result || data.results || [])); | |
// Sort problems by score in descending order | |
problems.sort((a, b) => (b.score || 0) - (a.score || 0)); | |
// Add tabs and content for each problem | |
problems.forEach((problem, index) => { | |
const key = `problem_${index + 1}`; | |
const isFirst = index === 0; | |
// Create tab | |
const tab = document.createElement('div'); | |
tab.className = `refined-problem-tab ${isFirst ? 'active' : ''}`; | |
tab.dataset.target = key; | |
// Format score as percentage | |
const score = typeof problem.score === 'number' ? | |
Math.round(problem.score * 100) : | |
'N/A'; | |
tab.textContent = `Option ${index + 1} (${score}%)`; | |
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; | |
// Problem title (using problematic as title) | |
const title = document.createElement('div'); | |
title.className = 'refined-problem-title'; | |
title.textContent = problem.problematic || 'Alternative Problem Statement'; | |
// Problem description | |
const description = document.createElement('div'); | |
description.className = 'refined-problem-description'; | |
description.textContent = problem.problem_description || problem.description || ''; | |
// Apply button | |
const applyButton = document.createElement('button'); | |
applyButton.className = 'apply-problem-btn'; | |
applyButton.textContent = 'Apply This Problem'; | |
applyButton.onclick = function() { | |
// Use the existing applyRefinedProblem function | |
applyRefinedProblem(problem.problem_description || problem.description || ''); | |
}; | |
// Add all elements to the problem div | |
problemDiv.appendChild(title); | |
problemDiv.appendChild(description); | |
problemDiv.appendChild(applyButton); | |
content.appendChild(problemDiv); | |
}); | |
// If no problems found, display a message | |
if (problems.length === 0) { | |
const noResultsDiv = document.createElement('div'); | |
noResultsDiv.className = 'refined-problem active'; | |
noResultsDiv.textContent = 'No problem alternatives were generated. Please try again.'; | |
content.appendChild(noResultsDiv); | |
} | |
} catch (error) { | |
console.error('Error displaying API response:', error); | |
// Display error message | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'refined-problem active'; | |
errorDiv.innerHTML = `<div class="refined-problem-title">Error</div> | |
<div class="refined-problem-description">There was an error processing the API response: ${error.message}</div>`; | |
content.appendChild(errorDiv); | |
} | |
// Show the container | |
apiResponseContainer.style.display = 'block'; | |
// Scroll to the container | |
apiResponseContainer.scrollIntoView({ behavior: 'smooth' }); | |
} | |
// 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 | |
}); | |
// Switch to Patentability Tools tab first | |
switchToPatentabilityTab(); | |
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 = ''; | |
// Collect problem descriptions for evaluation | |
const problemDescriptions = []; | |
for (const key in result) { | |
if (result.hasOwnProperty(key)) { | |
problemDescriptions.push(result[key].description); | |
} | |
} | |
// 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.dataset.problemIndex = problemDescriptions.indexOf(problem.description); | |
// Create tab content with placeholder for score | |
const tabTextSpan = document.createElement('span'); | |
tabTextSpan.textContent = `Option ${key.split('_')[2]}`; | |
// Create score element that will be updated later | |
const tabScoreSpan = document.createElement('span'); | |
tabScoreSpan.className = 'tab-score-indicator'; | |
tabScoreSpan.innerHTML = `<div class="tab-loading-spinner"></div>`; | |
tab.appendChild(tabTextSpan); | |
tab.appendChild(tabScoreSpan); | |
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 markdown-content'; | |
// Use Marked.js to render Markdown | |
try { | |
// Configure marked options for security | |
marked.setOptions({ | |
sanitize: false, // HTML is allowed but sanitized | |
breaks: true, // Interpret line breaks as <br> | |
gfm: true, // Use GitHub Flavored Markdown | |
headerIds: true, // Add IDs to headers for linking | |
}); | |
// Render the markdown | |
description.innerHTML = marked.parse(problem.description); | |
} catch (e) { | |
console.error('Error parsing markdown:', e); | |
description.textContent = problem.description; // Fallback to plain text | |
} | |
// Create score display with loading spinner initially | |
const scoreContainer = document.createElement('div'); | |
scoreContainer.className = 'problem-score-container'; | |
scoreContainer.innerHTML = ` | |
<div class="problem-score-loading"> | |
<div class="loading-spinner-small"></div> | |
<span>Evaluating specificity...</span> | |
</div> | |
<div class="problem-score" style="display: none;"></div> | |
`; | |
scoreContainer.dataset.problemIndex = problemDescriptions.indexOf(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(scoreContainer); | |
problemDiv.appendChild(applyButton); | |
content.appendChild(problemDiv); | |
isFirst = false; | |
} | |
} | |
// Show the container | |
refinedContainer.style.display = 'block'; | |
// Scroll to the container | |
refinedContainer.scrollIntoView({ behavior: 'smooth' }); | |
// If we have problem descriptions, evaluate them | |
if (problemDescriptions.length > 0) { | |
// Call the API to evaluate problem descriptions | |
fetch('/evaluate-problem-descriptions', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
'problem_descriptions': problemDescriptions | |
}) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
console.log('Problem evaluation results:', data); | |
// Update the UI with evaluation scores | |
if (Array.isArray(data)) { | |
data.forEach((evaluation, index) => { | |
const scoreContainers = document.querySelectorAll(`.problem-score-container[data-problem-index="${index}"]`); | |
scoreContainers.forEach(container => { | |
const loadingEl = container.querySelector('.problem-score-loading'); | |
const scoreEl = container.querySelector('.problem-score'); | |
if (loadingEl && scoreEl && evaluation.score !== undefined) { | |
// Hide loading spinner | |
loadingEl.style.display = 'none'; | |
// Format the score as a percentage | |
const scoreValue = (evaluation.score * 100).toFixed(1); | |
// Determine color based on score | |
let scoreColor = '#ff4d4d'; // Red for low scores | |
if (evaluation.score >= 0.7) { | |
scoreColor = '#4caf50'; // Green for high scores | |
} else if (evaluation.score >= 0.5) { | |
scoreColor = '#ff9800'; // Orange for medium scores | |
} | |
// Show score with appropriate styling | |
scoreEl.innerHTML = `<span style="font-weight: bold;">Specificity Score: <span style="color: ${scoreColor};">${scoreValue}%</span></span>`; | |
scoreEl.style.display = 'block'; | |
// Also update the score in the tab | |
const tabWithSameIndex = document.querySelector(`.refined-problem-tab[data-problem-index="${index}"]`); | |
if (tabWithSameIndex) { | |
const tabScoreSpan = tabWithSameIndex.querySelector('.tab-score-indicator'); | |
if (tabScoreSpan) { | |
tabScoreSpan.innerHTML = `<span style="color: ${scoreColor}; margin-left: 6px; font-size: 0.85em;">${scoreValue}%</span>`; | |
} | |
} | |
} | |
}); | |
}); | |
} else if (data.error) { | |
console.error('Error evaluating problems:', data.error); | |
// Update UI to show error state | |
document.querySelectorAll('.problem-score-loading').forEach(el => { | |
el.innerHTML = '<span style="color: #ff4d4d;">Evaluation failed</span>'; | |
}); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
// Update UI to show error state | |
document.querySelectorAll('.problem-score-loading').forEach(el => { | |
el.innerHTML = '<span style="color: #ff4d4d;">Evaluation failed</span>'; | |
}); | |
}); | |
} | |
} | |
// 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.'); | |
}); | |
} | |
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; | |
// Build the complete HTML structure | |
let cardContent = ` | |
<div class="key-issue-title">${issue.title}</div> | |
<div class="key-issue-description">${issue.description}</div> | |
<div style="font-weight: bold; margin-top: 10px;">Challenges:</div> | |
<div class="key-issue-challenges"></div> | |
<div class="key-issue-impact">Impact: ${issue.potential_impact}</div> | |
`; | |
issueCard.innerHTML = cardContent; | |
// Get the challenges list container that was just created | |
const challengesList = issueCard.querySelector('.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'); | |
// Add to selected key issues if not already there | |
if (!selectedKeyIssues.some(item => item.id === issue.id)) { | |
selectedKeyIssues.push(issue); | |
} | |
} else { | |
issueCard.classList.remove('selected'); | |
// Remove from selected key issues | |
selectedKeyIssues = selectedKeyIssues.filter(item => item.id !== issue.id); | |
} | |
// Update the comment area based on selections | |
updateCommentArea(); | |
}); | |
challengesList.appendChild(challengeTag); | |
}); | |
// The challenges list and impact are already in the HTML structure | |
// We just need to populate the challenges list with individual challenge tags | |
// 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 = ''; | |
} | |
} | |
}); | |
// 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(); | |
} | |
}); | |
// Generate Prior Art button (if exists) | |
const ribbonGeneratePriorArtButton = document.getElementById('ribbonGeneratePriorArtButton'); | |
if (ribbonGeneratePriorArtButton) { | |
ribbonGeneratePriorArtButton.addEventListener('click', function() { | |
submitSelectedKeyIssuesToAPI(); | |
}); | |
} | |
} | |
// 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'; | |
} | |
} | |