|
<!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( |
|
/<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>`; |
|
} |
|
); |
|
|
|
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( |
|
/<Model\s+name="([^"]+)"\s+version="([^"]+)">/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> |