This repository has no description
1export const LANDING_PAGE = `<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <title>Papers — Research Paper Summaries</title>
7 <style>
8 :root {
9 --bg: #0f0f0f;
10 --surface: #1a1a1a;
11 --text: #e0e0e0;
12 --dim: #888;
13 --accent: #6d9eeb;
14 --border: #333;
15 --green: #4caf50;
16 --mauve: #ce93d8;
17 }
18 * { box-sizing: border-box; margin: 0; padding: 0; }
19 body {
20 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
21 background: var(--bg);
22 color: var(--text);
23 line-height: 1.6;
24 padding: 2rem;
25 max-width: 960px;
26 margin: 0 auto;
27 }
28 header { margin-bottom: 2rem; }
29 header h1 { font-size: 1.75rem; font-weight: 700; }
30 header p { color: var(--dim); margin-top: 0.25rem; }
31 .actions { margin-bottom: 1.5rem; display: flex; gap: 0.75rem; }
32 .actions button {
33 padding: 0.5rem 1rem;
34 background: var(--accent);
35 color: var(--bg);
36 border: none;
37 border-radius: 6px;
38 font-weight: 600;
39 cursor: pointer;
40 }
41 .actions button:hover { opacity: 0.9; }
42 .actions button.secondary {
43 background: var(--surface);
44 color: var(--text);
45 border: 1px solid var(--border);
46 }
47 .paper-list { display: flex; flex-direction: column; gap: 0.75rem; }
48 .paper-card {
49 background: var(--surface);
50 border: 1px solid var(--border);
51 border-radius: 8px;
52 padding: 1rem 1.25rem;
53 cursor: pointer;
54 transition: border-color 0.15s;
55 text-decoration: none;
56 color: inherit;
57 display: block;
58 }
59 .paper-card:hover { border-color: var(--accent); }
60 .paper-card .title { font-weight: 600; margin-bottom: 0.25rem; }
61 .paper-card .meta { font-size: 0.8125rem; color: var(--dim); }
62 .paper-card .summary { font-size: 0.875rem; margin-top: 0.5rem; color: var(--text); }
63 .paper-card .domains { margin-top: 0.5rem; display: flex; gap: 0.375rem; flex-wrap: wrap; }
64 .paper-card .domain {
65 font-size: 0.6875rem;
66 padding: 0.125rem 0.375rem;
67 border-radius: 4px;
68 background: var(--border);
69 color: var(--dim);
70 }
71 .paper-card .venue-badge {
72 font-size: 0.6875rem;
73 padding: 0.125rem 0.375rem;
74 border-radius: 4px;
75 background: var(--mauve);
76 color: var(--bg);
77 font-weight: 600;
78 }
79 .empty { color: var(--dim); text-align: center; padding: 3rem; }
80 #poll-status { font-size: 0.8125rem; color: var(--dim); margin-left: 0.75rem; }
81 </style>
82</head>
83<body>
84 <header>
85 <h1>Papers</h1>
86 <p>Research paper summaries from the Paper Skygest feed</p>
87 </header>
88 <div class="actions">
89 <button onclick="pollFeed()">Poll Feed</button>
90 <span id="poll-status"></span>
91 </div>
92 <div id="papers" class="paper-list"></div>
93 <script>
94 const summaries = window.__SUMMARIES__ || [];
95 const container = document.getElementById('papers');
96 const pollStatus = document.getElementById('poll-status');
97
98 function render(summaries) {
99 if (summaries.length === 0) {
100 container.innerHTML = '<div class="empty">No papers indexed yet. Click "Poll Feed" to start.</div>';
101 return;
102 }
103 container.innerHTML = summaries.map(s => {
104 const domains = (s.domains || []).map(d => '<span class="domain">' + d + '</span>').join('');
105 const venue = s.venue ? '<span class="venue-badge">' + s.venue + '</span> ' : '';
106 const year = s.year ? ' (' + s.year + ')' : '';
107 const authors = (s.authors || []).length > 0
108 ? s.authors.length <= 3 ? s.authors.join(', ') : s.authors[0] + ' et al.'
109 : '';
110 return '<a class="paper-card" href="/paper/' + s.rkey + '">'
111 + '<div class="title">' + venue + escHtml(s.title) + year + '</div>'
112 + '<div class="meta">' + (authors ? escHtml(authors) + ' · ' : '') + escHtml(s.posterHandle) + ' · ' + s.paperUrl + '</div>'
113 + '<div class="summary">' + escHtml(s.summary).substring(0, 200) + '</div>'
114 + (domains ? '<div class="domains">' + domains + '</div>' : '')
115 + '</a>';
116 }).join('');
117 }
118
119 function escHtml(s) {
120 return (s || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
121 }
122
123 async function pollFeed() {
124 pollStatus.textContent = 'Polling...';
125 try {
126 const res = await fetch('/api/poll', { method: 'POST' });
127 const data = await res.json();
128 pollStatus.textContent = 'Polled ' + data.polled + ', new: ' + data.new + ', written: ' + data.written + (data.errors ? ', errors: ' + data.errors : '');
129 if (data.written > 0) setTimeout(() => location.reload(), 1500);
130 } catch (err) {
131 pollStatus.textContent = 'Error: ' + err.message;
132 }
133 }
134
135 render(summaries);
136 </script>
137</body>
138</html>`;