YchKhan's picture
Update static/js/script.js
ef05b36 verified
raw
history blame
128 kB
// 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()">&times;</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';
}
}