Spaces:
Sleeping
Sleeping
<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 | |
} | |
} | |
.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> |