Files
palette/web/templates/history.html
T
2026-09-08 19:26:07 -05:00

96 lines
3.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{template "head" .}}
{{template "topbar" .}}
<div class="page">
<div class="head-row">
<h1>Public pastes</h1>
<span class="count" id="count"></span>
<div class="spacer"></div>
<div class="search"><input id="filter" placeholder="filter…"></div>
</div>
<div class="float">
<table>
<colgroup><col style="width:130px"><col><col style="width:90px"><col style="width:80px"><col style="width:64px"><col style="width:96px"></colgroup>
<thead><tr><th>Paste</th><th>Description</th><th>Language</th><th>Size</th><th>Views</th><th>Created</th></tr></thead>
<tbody id="rows"></tbody>
</table>
<div class="empty" id="empty" style="display:none">No pastes yet. Create the first one.</div>
<div class="pager">
<span id="showing"></span>
<div class="pg" id="pg"></div>
</div>
</div>
</div>
<script>
const PER = 25;
let page = 1, total = 0;
const $ = id => document.getElementById(id);
function esc(s) { const d = document.createElement('div'); d.textContent = s == null ? '' : s; return d.innerHTML; }
function fmtSize(n) { if (n == null) return '—'; if (n < 1024) return n + ' B'; if (n < 1048576) return (n/1024).toFixed(1) + ' KB'; return (n/1048576).toFixed(1) + ' MB'; }
function ago(ts) {
const s = Math.floor(Date.now()/1000) - ts;
if (s < 60) return s + 's ago';
if (s < 3600) return Math.floor(s/60) + 'm ago';
if (s < 86400) return Math.floor(s/3600) + 'h ago';
return Math.floor(s/86400) + 'd ago';
}
async function load() {
const off = (page - 1) * PER;
const res = await fetch('/api/public?limit=' + PER + '&offset=' + off);
const data = await res.json();
total = data.total;
$('count').textContent = total.toLocaleString() + ' total';
const rows = $('rows');
if (data.items.length === 0) {
rows.innerHTML = '';
$('empty').style.display = 'block';
} else {
$('empty').style.display = 'none';
rows.innerHTML = data.items.map(it =>
`<tr class="row" data-href="/${esc(it.id)}"><td><a class="slug" href="/${esc(it.id)}">${esc(it.id)}</a></td>` +
`<td class="title-cell">${it.title ? esc(it.title) : '<span class=dim>—</span>'}</td>` +
`<td><span class="badge">${esc(it.language || 'text')}</span></td>` +
`<td class="dim">${fmtSize(it.size)}</td><td class="dim">${it.view_count}</td><td class="dim">${ago(it.created_at)}</td></tr>`
).join('');
}
const pages = Math.max(1, Math.ceil(total / PER));
$('showing').textContent = total === 0 ? 'Nothing here yet' :
`Showing ${off+1}${Math.min(off+PER, total)} of ${total.toLocaleString()} · page ${page} of ${pages}`;
const btns = [];
const add = (label, target, opts={}) => btns.push(`<button ${opts.on?'class="on"':''} ${opts.dis?'disabled':''} data-p="${target}">${label}</button>`);
add('', page-1, {dis: page===1});
const win = new Set([1, 2, page-1, page, page+1, pages]);
let last = 0;
for (let i = 1; i <= pages; i++) {
if (win.has(i)) {
if (last && i - last > 1) btns.push('<span class="dim">…</span>');
add(String(i), i, {on: i===page});
last = i;
}
}
add('', page+1, {dis: page===pages});
$('pg').innerHTML = btns.join('');
}
$('pg').addEventListener('click', e => {
const b = e.target.closest('button[data-p]');
if (!b || b.disabled) return;
page = parseInt(b.dataset.p);
load();
window.scrollTo(0, 0);
});
$('rows').addEventListener('click', e => {
const tr = e.target.closest('tr.row[data-href]');
if (!tr || e.target.closest('a')) return; // let real links (e.g. middle-click) work
window.location.href = tr.dataset.href;
});
$('filter').addEventListener('input', () => { page = 1; load(); });
load();
setInterval(load, 30000); // auto-refresh history every 30s
</script>
{{template "foot" .}}