const searchForm = document.getElementById('search-form'); const searchInput = document.getElementById('search-input'); const statusEl = document.getElementById('status'); const resultsEl = document.getElementById('search-results'); const resultsList = document.getElementById('results-list'); const graphContainer = document.getElementById('graph-container'); const infoPanel = document.getElementById('info-panel'); const infoContent = document.getElementById('info-content'); let network = null; function showStatus(msg, isError = false) { statusEl.textContent = msg; statusEl.className = isError ? 'error' : 'loading'; } function clearStatus() { statusEl.textContent = ''; statusEl.className = ''; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function formatAuthors(authors, maxLen = 3) { if (!authors || authors.length === 0) return 'Unknown'; const names = authors.map(a => a.name || 'Unknown'); if (names.length > maxLen) { return names.slice(0, maxLen).join(', ') + ' et al.'; } return names.join(', '); } function showResults(papers) { resultsList.innerHTML = ''; resultsEl.classList.remove('hidden'); graphContainer.innerHTML = ''; infoPanel.classList.add('hidden'); papers.forEach(paper => { const li = document.createElement('li'); const authors = formatAuthors(paper.authors); li.innerHTML = ` ${escapeHtml(paper.title || 'Untitled')} ${escapeHtml(authors)} — ${paper.year || 'n.d.'} — Citations: ${paper.citationCount || 0} `; li.addEventListener('click', () => { resultsEl.classList.add('hidden'); loadGraph(paper.paperId); }); resultsList.appendChild(li); }); } async function search(query) { showStatus('Searching...'); try { const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`); const data = await res.json(); clearStatus(); if (data.error) throw new Error(data.error); if (!data.papers || data.papers.length === 0) { showStatus('No papers found.', true); return; } showResults(data.papers); } catch (err) { showStatus(err.message, true); } } async function loadGraph(paperId) { showStatus('Loading reference graph...'); try { const res = await fetch(`/api/graph/${encodeURIComponent(paperId)}`); const data = await res.json(); clearStatus(); if (data.error) throw new Error(data.error); renderGraph(data); } catch (err) { showStatus(err.message, true); } } function renderGraph(graph) { graphContainer.innerHTML = ''; infoPanel.classList.add('hidden'); const container = document.createElement('div'); container.id = 'graph'; graphContainer.appendChild(container); if (graph.nodes.length <= 1) { container.innerHTML = `
No references found in OpenAlex for this paper yet.
This often happens with very recent preprints — OpenAlex hasn't ingested the reference list.
Authors: ${escapeHtml(node.authors)}
Year: ${node.year || 'n.d.'}
Citations: ${node.citationCount}
Tip: you can also double-click a node to re-center.
`; document.getElementById('recenter-btn').addEventListener('click', () => { loadGraph(node.id); }); } searchForm.addEventListener('submit', (e) => { e.preventDefault(); const query = searchInput.value.trim(); if (query) search(query); });