specialized-agents / static /iresearcher.html
pvanand's picture
Update static/iresearcher.html
d219d14 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Report Generator</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<link rel="stylesheet" href="styles.css">
<style>
:root {
--accent-color: #003366; /* Navy blue */
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0 auto;
padding: 20px;
background-color: #f0f2f5;
color: #333;
max-width: 1366px;
width: 100%;
}
#input-container {
margin-bottom: 20px;
background-color: #ffffff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.button-container {
justify-content: space-between;
height: 30px;
}
#output-container {
display: block;
}
#report-container, #sources-container {
max-width: 800px;
margin: 0 auto 30px;
background-color: #ffffff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table, th, td {
border: 1px solid #ddd;
}
th, td {
padding: 10px;
text-align: left;
}
textarea {
width: 100%;
padding: 12px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
resize: vertical;
box-sizing: border-box;
}
button {
padding: 12px 24px;
background-color: var(--accent-color);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
button:hover {
background-color: #002244;
}
.source-item {
margin-bottom: 20px;
padding: 15px;
background-color: #f9f9f9;
border: 1px solid #e0e0e0;
border-radius: 8px;
position: relative;
cursor: pointer;
transition: box-shadow 0.3s;
}
.source-item:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.source-url {
color: var(--accent-color);
text-decoration: none;
word-break: break-all;
font-weight: bold;
display: block;
margin-bottom: 10px;
cursor: pointer;
}
.source-content {
margin-top: 10px;
position: relative;
overflow: hidden;
}
.source-snippet {
max-height: 150px;
overflow: hidden;
}
.source-full {
display: none;
}
.expand-indicator {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 30px;
background: linear-gradient(to bottom, rgba(249,249,249,0), rgba(249,249,249,1));
display: flex;
align-items: center;
justify-content: center;
}
.expand-indicator::after {
content: '▼';
font-size: 14px;
color: #666;
}
.expanded .expand-indicator::after {
content: '▲';
}
.source-item:hover .expand-indicator {
background: linear-gradient(to bottom, rgba(249,249,249,0), rgba(249,249,249,0.9));
}
h1 {
color: var(--accent-color);
}
h2 {
color: var(--accent-color);
border-bottom: 2px solid var(--accent-color);
padding-bottom: 10px;
margin-top: 0;
}
@media (min-width: 768px) {
#output-container {
display: block;
}
#report-container, #sources-container {
flex: none;
}
}
@media (max-width: 767px) {
body {
padding: 10px;
width: 95%;
}
#input-container, #report-container, #sources-container {
padding: 15px;
}
.search-container {
width: 360px;
}
.title {
width: 300px;
font-weight: 500;
font-size: 50px !important
}
}
.centered-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.search-container {
display: flex;
margin-top: 20px;
}
.search-container input {
width: 500px;
padding: 10px 20px;
font-size: 16px;
border: 1px solid #bfcceb;
border-radius: 15px 0 0 15px;
outline: none;
color: #0e1f4b;
}
.search-container button {
width: 50px;
padding: 10px;
background: #ffffff;
border: none;
cursor: pointer;
transition: opacity 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0 15px 15px 0;
}
.search-container button:hover {
opacity: 0.5;
}
.search-container button svg {
width: 24px;
height: 24px;
}
::placeholder {
font-style: italic;
font: 1em sans-serif;
color: #7088c1;
opacity: 0.5;
}
.title {
font-weight: 700;
font-size: 64px;
background: linear-gradient(45deg, #3b82f6, #10b981);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-align: center;
margin: 0;
padding: 0 0 0.5rem;
}
.generate-btn {
white-space: nowrap;
}
.download-btn {
background-color: #30764c;
margin-top: 10px;
}
.download-btn:hover {
background-color: #2c8d55;
}
</style>
</head>
<body>
<div id="main-container">
<div id="initial-view" class="centered-container">
<h1 class="title">iResearcher</h1>
<div class="search-container">
<input type="text" id="description" placeholder="Your question/topic...">
<button onclick="generateReport()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="url(#gradient)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3b82f6" />
<stop offset="100%" stop-color="#10b981" />
</linearGradient>
</defs>
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</button>
</div>
</div>
<!-- This will be shown when a report is generated -->
<div id="report-view" style="display: none;">
<div id="input-container" style="display: none;">
<textarea id="description-textarea" rows="1" placeholder="Your question/topics"></textarea>
</div>
<div id="output-container">
<div id="report-container"></div>
<div id="sources-container"></div>
</div>
<div class="button-container">
<button id="downloadBtn" onclick="downloadHTML()" style="display: none;" title="Download HTML Report">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"/>
</svg>
</button>
</div>
</div>
</div>
<script>
async function generateReport() {
const description = document.getElementById('description').value || document.getElementById('description-textarea').value;
document.getElementById('initial-view').style.display = 'none';
document.getElementById('report-view').style.display = 'block';
document.getElementById('description-textarea').value = description;
const reportContainer = document.getElementById('report-container');
const sourcesContainer = document.getElementById('sources-container');
const downloadBtn = document.getElementById('downloadBtn');
reportContainer.innerHTML = 'Generating report...';
sourcesContainer.innerHTML = '';
downloadBtn.style.display = 'none';
try {
const response = await fetch('https://iresearcher-api.elevatics.cloud/generate_report', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/plain'
},
body: JSON.stringify({
description: description,
user_id: "",
user_name: "multi-agent-research",
internet: true,
output_format: "report_table",
data_format: "Structured data",
generate_charts: true,
output_as_md: true
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let markdown = '';
let metadata = '';
let isReadingMetadata = false;
while (true) {
const { value, done } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
console.log(chunk);
if (chunk.includes('<report-metadata>')) {
isReadingMetadata = true;
metadata = '';
}
if (isReadingMetadata) {
metadata += chunk;
if (chunk.includes('</report-metadata>')) {
isReadingMetadata = false;
processMetadata(metadata);
}
} else {
markdown += chunk;
renderMarkdown(markdown);
}
}
downloadBtn.style.display = 'inline-block';
} catch (error) {
reportContainer.innerHTML = `Error generating report: ${error.message}`;
}
}
function renderMarkdown(markdown) {
const reportContainer = document.getElementById('report-container');
const previousHeight = reportContainer.scrollHeight;
const reportContent = markdown.match(/<report>([\s\S]*)<\/report>/);
if (reportContent) {
reportContainer.innerHTML = marked.parse(reportContent[1]);
} else {
reportContainer.innerHTML = marked.parse(markdown);
}
const links = reportContainer.getElementsByTagName('a');
Array.from(links).forEach(link => {
link.setAttribute('target', '_blank');
});
const scripts = reportContainer.getElementsByTagName('script');
Array.from(scripts).forEach(script => {
const newScript = document.createElement('script');
newScript.textContent = script.textContent;
script.parentNode.replaceChild(newScript, script);
});
const plots = reportContainer.querySelectorAll('.js-plotly-plot');
plots.forEach(plot => {
Plotly.Plots.resize(plot);
});
scrollToNewContent(previousHeight);
}
function processMetadata(metadata) {
const sourcesContainer = document.getElementById('sources-container');
const metadataMatch = metadata.match(/all-text-with-urls: (.+)/);
if (metadataMatch) {
const metadataObj = JSON.parse(metadataMatch[1]);
sourcesContainer.innerHTML = '<h2>Sources</h2>';
metadataObj.forEach(([content, url]) => {
if (content.trim() !== "") {
const sourceItem = document.createElement('div');
sourceItem.className = 'source-item';
const snippet = content.length > 400 ? content.substring(0, 400) + '...' : content;
sourceItem.innerHTML = `
<a href="${url}" target="_blank" rel="noopener noreferrer" class="source-url">${url}</a>
<div class="source-content">
<div class="source-snippet">${marked.parse(snippet)}</div>
<div class="source-full">${marked.parse(content)}</div>
<div class="expand-indicator"></div>
</div>
`;
sourcesContainer.appendChild(sourceItem);
const sourceLinks = sourceItem.querySelectorAll('.source-content a');
sourceLinks.forEach(link => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
});
const sourceUrl = sourceItem.querySelector('.source-url');
const sourceContent = sourceItem.querySelector('.source-content');
const snippetDiv = sourceItem.querySelector('.source-snippet');
const fullDiv = sourceItem.querySelector('.source-full');
sourceContent.addEventListener('click', function(e) {
if (!sourceItem.classList.contains('expanded')) {
sourceItem.classList.add('expanded');
snippetDiv.style.display = 'none';
fullDiv.style.display = 'block';
} else if (e.clientY > sourceContent.getBoundingClientRect().bottom - 30) {
sourceItem.classList.remove('expanded');
snippetDiv.style.display = 'block';
fullDiv.style.display = 'none';
}
});
sourceUrl.addEventListener('click', function(e) {
e.stopPropagation();
});
}
});
} else {
sourcesContainer.innerHTML = '<h2>Sources</h2><p>No source information available.</p>';
}
}
function scrollToNewContent(previousHeight) {
const reportContainer = document.getElementById('report-container');
const newHeight = reportContainer.scrollHeight;
const scrollDifference = newHeight - previousHeight;
if (scrollDifference > 0) {
const viewportHeight = window.innerHeight;
const currentScrollPosition = window.pageYOffset;
const contentBottom = reportContainer.offsetTop + newHeight;
if (contentBottom > currentScrollPosition + viewportHeight - 200) {
window.scrollBy({
top: scrollDifference,
behavior: 'smooth'
});
}
}
}
function scrollToTop() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}
function sanitizeFileName(name) {
return name.replace(/[^a-z0-9\s]/gi, '').toLowerCase().replace(/\s+/g, '_').substring(0, 50) || 'generated_report';
}
function downloadHTML() {
try {
var css = `
:root {
--accent-color: #003366;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0 auto;
padding: 20px;
background-color: #f0f2f5;
color: #333;
max-width: 1366px;
}
#report-container, #sources-container {
max-width: 800px;
margin: 0 auto 30px;
background-color: #ffffff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table, th, td {
border: 1px solid #ddd;
}
th, td {
padding: 10px;
text-align: left;
}
h1, h2, h3, h4, h5, h6 {
color: var(--accent-color);
}
h2 {
border-bottom: 2px solid var(--accent-color);
padding-bottom: 10px;
margin-top: 0;
}
.source-item {
margin-bottom: 20px;
padding: 15px;
background-color: #f9f9f9;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.source-url {
color: var(--accent-color);
text-decoration: none;
word-break: break-all;
font-weight: bold;
display: block;
margin-bottom: 10px;
}
.source-content {
margin-top: 10px;
}
.source-snippet, .expand-indicator {
display: none;
}
.source-full {
display: block;
}
@media (max-width: 767px) {
body {
padding: 10px;
width: 95%;
}
#report-container, #sources-container {
padding: 15px;
}
}
`;
var htmlContent = '<!DOCTYPE html>\n<html lang="en">\n<head>\n' +
'<meta charset="UTF-8">\n' +
'<meta name="viewport" content="width=device-width, initial-scale=1.0">\n' +
'<title>Generated Report</title>\n' +
'<style>' + css + '</style>\n' +
'<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"><\/script>\n' +
'<script src="https://cdn.plot.ly/plotly-latest.min.js"><\/script>\n' +
'</head>\n<body>\n' +
'<div id="report-container">\n' + document.getElementById('report-container').innerHTML + '\n</div>\n' +
'<div id="sources-container">\n' + document.getElementById('sources-container').innerHTML + '\n</div>\n' +
'</body>\n</html>';
var fileName = sanitizeFileName(document.querySelector('#report-container h1, #report-container h2, #report-container h3')?.textContent || 'generated_report');
var blob = new Blob([htmlContent], { type: 'text/html' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = fileName + '.html';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('Error downloading HTML:', error);
}
}
document.addEventListener('DOMContentLoaded', function() {
const descriptionInput = document.getElementById('description');
const initialView = document.getElementById('initial-view');
function handleEnterKey(event) {
if (event.key === 'Enter' &&
this.value.trim() !== '' &&
initialView.style.display !== 'none') {
event.preventDefault();
generateReport();
}
}
descriptionInput.addEventListener('keydown', handleEnterKey);
});
window.addEventListener('resize', function() {
const plots = document.querySelectorAll('.js-plotly-plot');
plots.forEach(plot => {
Plotly.Plots.resize(plot);
});
});
</script>
</body>
</html>