() => { let PAPERS = []; let cur = null; let pile = []; // indices into items, top of pile first let held = []; // indices, newest first let items = []; // [{text, kind}] let busy = false; let jid = null; let epoch = 0; let animLock = false; const $ = (id) => document.getElementById(id); function esc(t) { const d = document.createElement('div'); d.textContent = t; return d.innerHTML; } function shuffle(a) { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } function counter() { $('ivx-counter').textContent = pile.length + ' in the pile \u00b7 ' + held.length + ' held'; $('ivx-gen').disabled = busy || held.length === 0; $('ivx-heldhead').hidden = held.length === 0; } function showTop() { const card = $('ivx-card'); card.classList.remove('fly-left', 'fly-right'); if (!pile.length) { card.querySelector('.kind').textContent = ''; card.querySelector('.txt').textContent = held.length ? 'Pile empty. Write, or remove a held card to reconsider it.' : 'Pile empty.'; counter(); return; } const it = items[pile[0]]; card.querySelector('.kind').textContent = it.kind; card.querySelector('.txt').textContent = it.text; counter(); } function renderHeld() { const box = $('ivx-held'); box.innerHTML = ''; held.forEach((idx) => { const el = document.createElement('div'); el.className = 'hcard'; el.innerHTML = '
' + esc(items[idx].text) + '
'; el.querySelector('.rm').addEventListener('click', () => { held = held.filter(h => h !== idx); pile.push(idx); renderHeld(); showTop(); }); box.appendChild(el); }); counter(); } function verdict(dir) { if (animLock || !pile.length) return; animLock = true; const card = $('ivx-card'); card.classList.add(dir === 'hold' ? 'fly-right' : 'fly-left'); setTimeout(() => { const idx = pile.shift(); if (dir === 'hold') { held.unshift(idx); renderHeld(); } else { pile.push(idx); } showTop(); animLock = false; }, 240); } function pickPaper(pid) { cur = PAPERS.find(p => p.id === pid); epoch += 1; items = cur.rhetorical.map(t => ({ text: t, kind: 'rhetorical' })) .concat(cur.content.map(t => ({ text: t, kind: 'content' }))); pile = shuffle(items.map((_, i) => i)); held = []; document.querySelectorAll('#ivx-tabs button').forEach(b => b.classList.toggle('on', b.dataset.pid === pid)); $('ivx-ptitle').textContent = cur.title; $('ivx-out').hidden = true; $('ivx-empty').hidden = false; $('ivx-think').textContent = ''; $('ivx-abs').textContent = ''; $('ivx-bars').innerHTML = ''; renderHeld(); showTop(); } function setStatus(t) { const s = $('ivx-status'); if (!t) { s.hidden = true; s.textContent = ''; return; } s.hidden = false; s.textContent = t; } function bars(sel, scores) { return sel.map((t, i) => { const s = scores[String(i + 1)] || scores[i + 1]; const pct = s ? s / 5 * 100 : 0; const color = (s || 0) >= 4 ? 'var(--hold)' : (s || 0) === 3 ? '#d9a514' : '#cc4b4b'; return '
' + esc(t) + '
' + (s ? s + '/5' : '?') + '
'; }).join(''); } function bridgeCall(inputId, btnId, value) { const inp = document.querySelector('#' + inputId + ' textarea, #' + inputId + ' input'); const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value') || Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value'); setter.set.call(inp, value); inp.dispatchEvent(new Event('input', { bubbles: true })); setTimeout(() => document.querySelector('#' + btnId + ' button, button#' + btnId).click(), 30); } function outValue() { const el = document.querySelector('#ivx-bridge-out textarea, #ivx-bridge-out input'); return el ? el.value : ''; } async function restCall(name, args) { const r = await fetch('gradio_api/call/' + name, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data: args }) }); const j = await r.json(); const r2 = await fetch('gradio_api/call/' + name + '/' + j.event_id); const t = await r2.text(); const lines = t.split('\n').filter(x => x.startsWith('data:')); if (!lines.length) throw new Error('no data from ' + name); return JSON.parse(lines[lines.length - 1].slice(5))[0]; } function run() { if (busy || !cur || !held.length) return; busy = true; const myEpoch = epoch; const sel = held.map(i => items[i].text); jid = 'j' + Math.random().toString(36).slice(2); $('ivx-empty').hidden = true; $('ivx-out').hidden = false; $('ivx-thinkbox').open = true; $('ivx-think').textContent = ''; $('ivx-abs').textContent = ''; $('ivx-bars').innerHTML = ''; setStatus('waking the model\u2026'); counter(); const lastRaw = outValue(); bridgeCall('ivx-bridge-in', 'ivx-bridge-btn', JSON.stringify({ jid: jid, paper_id: cur.id, intuitions: sel })); const poll = setInterval(async () => { if (epoch !== myEpoch) { clearInterval(poll); finish(); return; } let raw = outValue(); if (!raw || raw === lastRaw) { try { raw = await restCall('peek', [jid]); } catch (e) { /* keep polling */ } } if (!raw || raw === lastRaw) return; let ob; try { ob = JSON.parse(raw); } catch (e) { return; } if (ob.jid !== jid) return; if (ob.phase === 'error') { setStatus(ob.msg); clearInterval(poll); finish(); return; } if (ob.thinking) $('ivx-think').textContent = ob.thinking; if (ob.abstract) $('ivx-abs').textContent = ob.abstract; setStatus(ob.abstract ? 'writing the abstract\u2026' : 'thinking\u2026'); const tb = $('ivx-think'); tb.scrollTop = tb.scrollHeight; if (ob.phase === 'done') { $('ivx-bars').innerHTML = bars(sel, ob.scores || {}); $('ivx-thinkbox').open = false; setStatus('done'); clearInterval(poll); finish(); } }, 800); function finish() { busy = false; counter(); } } async function init() { try { PAPERS = JSON.parse(await restCall('papers', [])); const tabs = $('ivx-tabs'); PAPERS.forEach(p => { const b = document.createElement('button'); b.textContent = p.title.length > 42 ? p.title.slice(0, 40) + '\u2026' : p.title; b.dataset.pid = p.id; b.addEventListener('click', () => pickPaper(p.id)); tabs.appendChild(b); }); $('ivx-gen').addEventListener('click', run); $('ivx-pass').addEventListener('click', () => verdict('pass')); $('ivx-hold').addEventListener('click', () => verdict('hold')); document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') verdict('pass'); if (e.key === 'ArrowRight') verdict('hold'); }); $('ivx-ver').textContent = 'VERSION'; pickPaper(PAPERS[0].id); } catch (e) { const pt = $('ivx-ptitle'); if (pt) pt.textContent = 'The page failed to load its data. Refresh to retry. (' + e.message + ')'; } } const waiter = setInterval(() => { if (document.getElementById('ivx-app')) { clearInterval(waiter); init(); } }, 200); }