/** * summarize.js — Custom handler for the Summarize Document tool. * * Handles file upload (via api/extract.php), corpus slice toggles, * engine selection, JSON submission, and NDJSON streaming response. */ (function () { 'use strict'; // ── Element refs ────────────────────────────────────────────────────────── var form = document.getElementById('sumForm'); var runBtn = document.getElementById('sumRunButton'); var statusEl = document.getElementById('sumStatus'); var resultsEl = document.getElementById('sumResults'); var traceList = document.getElementById('traceList'); var textarea = document.getElementById('sumInput'); var uploadZone = document.getElementById('sumUploadZone'); var uploadInput = document.getElementById('sumUploadInput'); var uploadPrompt = document.getElementById('sumUploadPrompt'); var uploadFileInfo = document.getElementById('sumUploadFileInfo'); var uploadFileList = document.getElementById('sumUploadFileList'); var uploadClear = document.getElementById('sumUploadClear'); // ── State ───────────────────────────────────────────────────────────────── var _extractedFiles = []; // [{ name, text, chars }] var _currentLang = 'en'; var _lastPayload = null; // ── Lang switcher ───────────────────────────────────────────────────────── document.querySelectorAll('.sum-lang-btn').forEach(function (btn) { btn.addEventListener('click', function () { document.querySelectorAll('.sum-lang-btn').forEach(function (b) { b.classList.toggle('is-active', b === btn); }); _currentLang = btn.dataset.lang || 'en'; }); }); // ── Corpus slice toggles ────────────────────────────────────────────────── document.querySelectorAll('.sum-slice').forEach(function (btn) { btn.addEventListener('click', function () { var on = btn.classList.toggle('is-on'); btn.setAttribute('aria-pressed', String(on)); var badge = btn.querySelector('.dr-slice__badge'); if (badge) badge.textContent = on ? 'on' : 'off'; }); }); function activeSlices() { var out = []; document.querySelectorAll('.sum-slice.is-on').forEach(function (btn) { if (btn.dataset.slice) out.push(btn.dataset.slice); }); return out; } // ── File upload ─────────────────────────────────────────────────────────── if (uploadZone) { uploadZone.addEventListener('dragover', function (e) { e.preventDefault(); uploadZone.classList.add('is-drag-over'); }); uploadZone.addEventListener('dragleave', function (e) { if (!uploadZone.contains(e.relatedTarget)) { uploadZone.classList.remove('is-drag-over'); } }); uploadZone.addEventListener('drop', function (e) { e.preventDefault(); uploadZone.classList.remove('is-drag-over'); if (e.dataTransfer && e.dataTransfer.files.length) { handleFiles(e.dataTransfer.files); } }); // Stop label-for and the input itself from bubbling into the zone click // handler — otherwise the picker opens twice (native + programmatic). var browseLabel = uploadZone.querySelector('label[for="' + (uploadInput && uploadInput.id) + '"]'); if (browseLabel) { browseLabel.addEventListener('click', function (e) { e.stopPropagation(); }); } if (uploadInput) { uploadInput.addEventListener('click', function (e) { e.stopPropagation(); }); } uploadZone.addEventListener('click', function (e) { if (e.target === uploadClear || (uploadClear && uploadClear.contains(e.target))) return; if (e.target === uploadInput) return; var lbl = e.target.closest && e.target.closest('label'); if (lbl && lbl.getAttribute('for') === uploadInput.id) return; if (uploadInput) uploadInput.click(); }); } if (uploadInput) { uploadInput.addEventListener('change', function () { if (uploadInput.files && uploadInput.files.length) { handleFiles(uploadInput.files); } }); } if (uploadClear) { uploadClear.addEventListener('click', function (e) { e.stopPropagation(); resetUpload(); }); } function resetUpload() { _extractedFiles = []; if (uploadInput) uploadInput.value = ''; if (uploadPrompt) uploadPrompt.classList.remove('is-hidden'); if (uploadFileInfo) uploadFileInfo.classList.add('is-hidden'); if (uploadFileList) uploadFileList.innerHTML = ''; } function handleFiles(fileList) { var files = Array.from(fileList).slice(0, 5); if (!files.length) return; setStatus('Extracting text from ' + files.length + ' file(s)…'); setBusy(true); var promises = files.map(function (file) { var fd = new FormData(); fd.append('file', file); return fetch('api/extract.php', { method: 'POST', credentials: 'same-origin', body: fd }) .then(function (r) { return r.json(); }) .then(function (data) { if (!data.ok) throw new Error(data.error || 'Extraction failed for ' + file.name); return { name: file.name, text: data.text || '', chars: data.chars || 0 }; }); }); Promise.all(promises) .then(function (results) { _extractedFiles = results; renderFileList(results); setStatus(''); setBusy(false); }) .catch(function (err) { setStatus('Error: ' + err.message); setBusy(false); }); } function renderFileList(files) { if (!uploadFileList) return; uploadFileList.innerHTML = files.map(function (f) { return '
' + esc(s.detail || '') + '
' + esc(data.what_we_found) + '
' + '' + esc(data.next_practical_step) + '
' + '' + 'Summary enriched with relevant passages from the Do Better Norge legal corpus.' + '
' ); } if (data.disclaimer) { sections.push('' + esc(data.disclaimer) + '
'); } resultsEl.innerHTML = sections.join(''); // Update reasoning panel trace if (traceList && Array.isArray(data.trace) && data.trace.length) { traceList.innerHTML = data.trace.map(function (item) { return '' + esc(item.detail || '') + '
' + esc(msg) + '