Skip to content

Instantly share code, notes, and snippets.

@rcanand
Last active January 6, 2026 03:08
Show Gist options
  • Select an option

  • Save rcanand/e647e0142cff6e817deafe5eed73b2fb to your computer and use it in GitHub Desktop.

Select an option

Save rcanand/e647e0142cff6e817deafe5eed73b2fb to your computer and use it in GitHub Desktop.
Asta report viewer

The html file is used as a standalone html viewer to locally view research reports generated by Allen AI's asta (https:// asta.allen.ai) by clicking on "Generate report". You can then download the report as json by clicking on "Download Report" when the report is complete.

Download the html file above, and open it in your browser. Drag one of the reports you downloaded from asta into the box on the html page. You will see it rendered nicely and locally.

Disclaimer: I am not affiliated with Allen AI or their asta product in any way - just a big fan and use this feature (their deep research) a lot.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Asta Report Viewer</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #0d1117;
color: #c9d1d9;
line-height: 1.7;
margin: 0;
padding: 0;
}
.container {
max-width: 900px;
margin: 0 auto;
padding: 40px 24px;
}
/* File picker styles */
.file-picker {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 60vh;
text-align: center;
}
.file-picker h1 {
color: #f0f6fc;
font-size: 2rem;
margin-bottom: 8px;
}
.file-picker p {
color: #8b949e;
margin-bottom: 32px;
}
.file-picker-area {
border: 2px dashed #30363d;
border-radius: 12px;
padding: 48px;
cursor: pointer;
transition: all 0.2s ease;
background-color: #161b22;
max-width: 500px;
width: 100%;
}
.file-picker-area:hover,
.file-picker-area.dragover {
border-color: #58a6ff;
background-color: #1c2128;
}
.file-picker-area svg {
width: 64px;
height: 64px;
color: #8b949e;
margin-bottom: 16px;
}
.file-picker-area h3 {
color: #f0f6fc;
margin: 0 0 8px 0;
}
.file-picker-area span {
color: #8b949e;
font-size: 0.9rem;
}
#fileInput {
display: none;
}
.btn-browse {
background: linear-gradient(135deg, #238636 0%, #2ea043 100%);
color: #fff;
border: none;
padding: 12px 24px;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
margin-top: 16px;
transition: transform 0.1s ease;
}
.btn-browse:hover {
transform: scale(1.02);
}
.btn-browse:active {
transform: scale(0.98);
}
/* Report styles */
.report-container {
display: none;
}
.report-container.visible {
display: block;
}
.file-picker.hidden {
display: none;
}
header {
border-bottom: 1px solid #30363d;
padding-bottom: 24px;
margin-bottom: 32px;
}
.header-actions {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 12px;
}
.source-badge {
display: inline-block;
background: linear-gradient(135deg, #238636 0%, #1f6feb 100%);
color: #fff;
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 12px;
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.btn-load-new {
background-color: #21262d;
color: #c9d1d9;
border: 1px solid #30363d;
padding: 8px 16px;
border-radius: 6px;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.2s ease;
}
.btn-load-new:hover {
background-color: #30363d;
border-color: #8b949e;
}
h1 {
color: #f0f6fc;
font-size: 2rem;
font-weight: 600;
margin: 0 0 16px 0;
}
.query-box {
background-color: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 16px;
margin-top: 16px;
}
.query-label {
color: #8b949e;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.query-text {
color: #58a6ff;
font-size: 1.1rem;
font-style: italic;
}
.toc {
background-color: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 20px;
margin-bottom: 32px;
}
.toc h2 {
color: #f0f6fc;
font-size: 1rem;
margin: 0 0 12px 0;
}
.toc ul {
list-style: none;
padding: 0;
margin: 0;
}
.toc li {
margin: 8px 0;
}
.toc a {
color: #58a6ff;
text-decoration: none;
font-size: 0.95rem;
cursor: pointer;
pointer-events: auto;
}
.toc a:hover {
text-decoration: underline;
color: #79c0ff;
}
.section {
background-color: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 24px;
margin-bottom: 24px;
}
.section h2 {
color: #f0f6fc;
font-size: 1.5rem;
font-weight: 600;
margin: 0 0 16px 0;
padding-bottom: 12px;
border-bottom: 1px solid #30363d;
}
.tldr {
background-color: #1c2128;
border-left: 3px solid #58a6ff;
padding: 12px 16px;
margin-bottom: 20px;
border-radius: 0 6px 6px 0;
}
.tldr-label {
color: #58a6ff;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 6px;
}
.tldr-text {
color: #8b949e;
font-size: 0.95rem;
margin: 0;
}
.section-text {
color: #c9d1d9;
}
.section-text p {
margin: 16px 0;
}
.section-text strong {
color: #f0f6fc;
}
.section-text ul,
.section-text ol {
margin: 16px 0;
padding-left: 24px;
}
.section-text li {
margin: 12px 0;
}
.citation-inline {
color: #58a6ff;
text-decoration: none;
font-weight: 500;
background-color: rgba(88, 166, 255, 0.1);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
cursor: pointer;
pointer-events: auto;
}
.citation-inline:hover {
background-color: rgba(88, 166, 255, 0.2);
text-decoration: underline;
}
a {
cursor: pointer;
pointer-events: auto;
}
.model-tag {
color: #a371f7;
font-size: 0.75em;
opacity: 0.6;
font-style: italic;
}
.citations-section {
margin-top: 24px;
padding-top: 20px;
border-top: 1px solid #30363d;
}
.citations-section h3 {
color: #8b949e;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0 0 16px 0;
}
.citation-card {
background-color: #1c2128;
border: 1px solid #30363d;
border-radius: 6px;
padding: 16px;
margin-bottom: 12px;
}
.citation-card:last-child {
margin-bottom: 0;
}
.citation-title {
color: #58a6ff;
font-weight: 600;
font-size: 1rem;
margin-bottom: 8px;
text-decoration: none;
display: block;
cursor: pointer;
pointer-events: auto;
}
.citation-title:hover {
text-decoration: underline;
color: #79c0ff;
}
.citation-meta {
color: #8b949e;
font-size: 0.85rem;
margin-bottom: 8px;
}
.citation-meta span {
margin-right: 16px;
}
.citation-snippet {
color: #8b949e;
font-size: 0.9rem;
font-style: italic;
background-color: #0d1117;
padding: 12px;
border-radius: 4px;
border-left: 2px solid #30363d;
margin-top: 8px;
}
.table-container {
margin-top: 24px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
th {
background-color: #1c2128;
color: #f0f6fc;
font-weight: 600;
text-align: left;
padding: 12px;
border: 1px solid #30363d;
position: sticky;
top: 0;
}
td {
padding: 12px;
border: 1px solid #30363d;
vertical-align: top;
}
tr:nth-child(even) td {
background-color: #161b22;
}
tr:nth-child(odd) td {
background-color: #0d1117;
}
.paper-link {
color: #58a6ff;
text-decoration: none;
cursor: pointer;
pointer-events: auto;
}
.paper-link:hover {
text-decoration: underline;
color: #79c0ff;
}
footer {
text-align: center;
padding: 32px;
color: #484f58;
font-size: 0.85rem;
border-top: 1px solid #30363d;
margin-top: 40px;
}
footer a {
color: #58a6ff;
text-decoration: none;
cursor: pointer;
pointer-events: auto;
}
footer a:hover {
text-decoration: underline;
color: #79c0ff;
}
.error-message {
background-color: #490202;
border: 1px solid #f85149;
color: #f85149;
padding: 16px;
border-radius: 8px;
margin-top: 16px;
text-align: left;
}
/* Collapsible snippets */
.snippets-toggle {
color: #8b949e;
font-size: 0.85rem;
cursor: pointer;
margin-top: 8px;
display: inline-block;
}
.snippets-toggle:hover {
color: #58a6ff;
}
.snippets-container {
display: none;
}
.snippets-container.expanded {
display: block;
}
@media (max-width: 600px) {
.container {
padding: 20px 16px;
}
h1 {
font-size: 1.5rem;
}
.section {
padding: 16px;
}
.file-picker-area {
padding: 32px 24px;
}
}
</style>
</head>
<body>
<div class="container">
<!-- File Picker -->
<div class="file-picker" id="filePicker">
<h1>Asta Report Viewer</h1>
<p>
View exported JSON reports from Allen AI's Asta (formerly
ScholarQA)
</p>
<div class="file-picker-area" id="dropZone">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<h3>Drop your JSON file here</h3>
<span>or click to browse</span>
<button
class="btn-browse"
onclick="event.stopPropagation(); document.getElementById('fileInput').click()"
>
Choose File
</button>
</div>
<input
type="file"
id="fileInput"
accept=".json,application/json"
/>
<div id="errorContainer"></div>
</div>
<!-- Report Content -->
<div class="report-container" id="reportContainer">
<header>
<div class="header-actions">
<span class="source-badge">Asta Report</span>
<button class="btn-load-new" onclick="resetViewer()">
Load Another File
</button>
</div>
<h1 id="reportTitle"></h1>
<div class="query-box">
<div class="query-label">Research Query</div>
<div class="query-text" id="reportQuery"></div>
</div>
</header>
<nav class="toc" id="toc">
<h2>Table of Contents</h2>
<ul id="tocList"></ul>
</nav>
<main id="sectionsContainer"></main>
<footer>
Report exported from
<a href="https://asta.apps.allenai.org/" target="_blank"
>Allen AI Asta</a
>
</footer>
</div>
</div>
<script>
// Configure marked to allow HTML passthrough for citation links
marked.use({
breaks: true,
gfm: true,
});
// File handling
const dropZone = document.getElementById("dropZone");
const fileInput = document.getElementById("fileInput");
const filePicker = document.getElementById("filePicker");
const reportContainer = document.getElementById("reportContainer");
const errorContainer = document.getElementById("errorContainer");
// Drag and drop handlers
dropZone.addEventListener("dragover", (e) => {
e.preventDefault();
dropZone.classList.add("dragover");
});
dropZone.addEventListener("dragleave", () => {
dropZone.classList.remove("dragover");
});
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
dropZone.classList.remove("dragover");
const file = e.dataTransfer.files[0];
if (file) handleFile(file);
});
dropZone.addEventListener("click", (e) => {
if (e.target.tagName !== "BUTTON") {
fileInput.click();
}
});
fileInput.addEventListener("change", (e) => {
const file = e.target.files[0];
if (file) handleFile(file);
});
function handleFile(file) {
errorContainer.innerHTML = "";
if (!file.name.endsWith(".json")) {
showError("Please select a JSON file.");
return;
}
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
renderReport(data);
} catch (err) {
showError("Failed to parse JSON file: " + err.message);
}
};
reader.onerror = () => {
showError("Failed to read file.");
};
reader.readAsText(file);
}
function showError(message) {
errorContainer.innerHTML = `<div class="error-message">${escapeHtml(
message
)}</div>`;
}
function resetViewer() {
filePicker.classList.remove("hidden");
reportContainer.classList.remove("visible");
fileInput.value = "";
errorContainer.innerHTML = "";
}
function renderReport(data) {
// Update title and query
document.getElementById("reportTitle").textContent =
data.reportTitle || "Untitled Report";
document.getElementById("reportQuery").textContent =
data.query || "No query specified";
document.title =
(data.reportTitle || "Asta Report") + " - Viewer";
// Build TOC
const tocList = document.getElementById("tocList");
tocList.innerHTML = "";
(data.sections || []).forEach((section, idx) => {
const li = document.createElement("li");
const a = document.createElement("a");
a.href = `#section-${idx}`;
a.textContent = section.title || `Section ${idx + 1}`;
li.appendChild(a);
tocList.appendChild(li);
});
// Render sections
const sectionsContainer =
document.getElementById("sectionsContainer");
sectionsContainer.innerHTML = "";
(data.sections || []).forEach((section, idx) => {
const sectionEl = document.createElement("section");
sectionEl.className = "section";
sectionEl.id = `section-${idx}`;
// Section title
const h2 = document.createElement("h2");
h2.textContent = section.title || `Section ${idx + 1}`;
sectionEl.appendChild(h2);
// TLDR
if (section.tldr) {
const tldr = document.createElement("div");
tldr.className = "tldr";
tldr.innerHTML = `
<div class="tldr-label">TL;DR</div>
<p class="tldr-text">${escapeHtml(section.tldr)}</p>
`;
sectionEl.appendChild(tldr);
}
// Section text
if (section.text) {
const textDiv = document.createElement("div");
textDiv.className = "section-text";
// First parse markdown, then process custom tags
let htmlContent = marked.parse(section.text.trim());
htmlContent = processCustomTags(htmlContent);
textDiv.innerHTML = htmlContent;
sectionEl.appendChild(textDiv);
}
// Table (if present)
if (section.table) {
const tableContainer = document.createElement("div");
tableContainer.className = "table-container";
tableContainer.appendChild(renderTable(section.table));
sectionEl.appendChild(tableContainer);
}
// Citations
if (section.citations && section.citations.length > 0) {
const citationsDiv = document.createElement("div");
citationsDiv.className = "citations-section";
citationsDiv.innerHTML = "<h3>Sources</h3>";
section.citations.forEach((citation, citIdx) => {
citationsDiv.appendChild(
renderCitation(citation, `${idx}-${citIdx}`)
);
});
sectionEl.appendChild(citationsDiv);
}
sectionsContainer.appendChild(sectionEl);
});
// Show report, hide picker
filePicker.classList.add("hidden");
reportContainer.classList.add("visible");
}
function processCustomTags(html) {
// Replace Paper tags (might be HTML-encoded by marked)
// Handle both encoded and non-encoded versions
html = html.replace(
/&lt;Paper\s+corpusId="(\d+)"\s+paperTitle="([^"]+)"[^&]*&gt;&lt;\/Paper&gt;/g,
(match, corpusId, title) => {
return `<a class="citation-inline" href="https://www.semanticscholar.org/p/${corpusId}" target="_blank" rel="noopener noreferrer">${escapeHtml(
title
)}</a>`;
}
);
html = html.replace(
/<Paper\s+corpusId="(\d+)"\s+paperTitle="([^"]+)"[^>]*><\/Paper>/g,
(match, corpusId, title) => {
return `<a class="citation-inline" href="https://www.semanticscholar.org/p/${corpusId}" target="_blank" rel="noopener noreferrer">${escapeHtml(
title
)}</a>`;
}
);
// Replace Model tags (both encoded and non-encoded)
html = html.replace(
/&lt;Model\s+name="([^"]+)"\s+version="([^"]+)"&gt;/g,
(match, name, version) => {
return `<span class="model-tag">[${escapeHtml(
name
)}]</span>`;
}
);
html = html.replace(
/<Model\s+name="([^"]+)"\s+version="([^"]+)">/g,
(match, name, version) => {
return `<span class="model-tag">[${escapeHtml(
name
)}]</span>`;
}
);
return html;
}
function renderCitation(citation, uniqueId) {
const card = document.createElement("div");
card.className = "citation-card";
const paper = citation.paper || {};
const corpusId = citation.corpusId || paper.corpusId;
const ssLink = corpusId
? `https://www.semanticscholar.org/p/${corpusId}`
: "#";
let html = `
<a class="citation-title" href="${ssLink}" target="_blank">
${escapeHtml(paper.title || citation.id || "Untitled")}
</a>
<div class="citation-meta">
`;
if (paper.year) {
html += `<span>๐Ÿ“… ${paper.year}</span>`;
}
if (paper.venue) {
html += `<span>๐Ÿ“ ${escapeHtml(paper.venue)}</span>`;
}
if (paper.nCitations !== undefined) {
html += `<span>๐Ÿ“š ${paper.nCitations} citations</span>`;
}
html += "</div>";
// Authors
if (paper.authors && paper.authors.length > 0) {
const authorNames = paper.authors
.map((a) => a.name)
.join(", ");
html += `<div class="citation-meta">๐Ÿ‘ค ${escapeHtml(
authorNames
)}</div>`;
}
// Snippets (collapsible if more than one)
if (citation.snippets && citation.snippets.length > 0) {
if (citation.snippets.length === 1) {
html += `<div class="citation-snippet">${escapeHtml(
citation.snippets[0]
)}</div>`;
} else {
html += `
<div class="citation-snippet">${escapeHtml(
citation.snippets[0]
)}</div>
<span class="snippets-toggle" onclick="toggleSnippets('${uniqueId}')">
โ–ถ Show ${
citation.snippets.length - 1
} more snippet(s)
</span>
<div class="snippets-container" id="snippets-${uniqueId}">
`;
for (let i = 1; i < citation.snippets.length; i++) {
html += `<div class="citation-snippet">${escapeHtml(
citation.snippets[i]
)}</div>`;
}
html += "</div>";
}
}
card.innerHTML = html;
return card;
}
function toggleSnippets(id) {
const container = document.getElementById(`snippets-${id}`);
const toggle = container.previousElementSibling;
if (container.classList.contains("expanded")) {
container.classList.remove("expanded");
const count =
container.querySelectorAll(".citation-snippet").length;
toggle.innerHTML = `โ–ถ Show ${count} more snippet(s)`;
} else {
container.classList.add("expanded");
toggle.innerHTML = "โ–ผ Hide snippets";
}
}
function renderTable(tableData) {
const table = document.createElement("table");
// Header
const thead = document.createElement("thead");
const headerRow = document.createElement("tr");
// First column for paper name
const paperTh = document.createElement("th");
paperTh.textContent = "Paper";
headerRow.appendChild(paperTh);
// Other columns
(tableData.columns || []).forEach((col) => {
const th = document.createElement("th");
th.textContent = col.name || "";
th.title = col.description || "";
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Body
const tbody = document.createElement("tbody");
(tableData.rows || []).forEach((row) => {
const tr = document.createElement("tr");
// Paper name cell
const paperTd = document.createElement("td");
if (row.paperCorpusId) {
const link = document.createElement("a");
link.className = "paper-link";
link.href = `https://www.semanticscholar.org/p/${row.paperCorpusId}`;
link.target = "_blank";
link.textContent = row.displayValue || "View Paper";
paperTd.appendChild(link);
} else {
paperTd.textContent = row.displayValue || "";
}
tr.appendChild(paperTd);
// Other cells
(tableData.columns || []).forEach((col) => {
const td = document.createElement("td");
const cellKey = `${row.id}_${col.id}`;
const cellData = tableData.cells?.[cellKey];
td.textContent = cellData?.displayValue || "";
tr.appendChild(td);
});
tbody.appendChild(tr);
});
table.appendChild(tbody);
return table;
}
function escapeHtml(text) {
if (!text) return "";
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment