/**
* 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);
}
});
uploadZone.addEventListener('click', function (e) {
if (e.target === uploadClear || (uploadClear && uploadClear.contains(e.target))) return;
if (e.target.tagName === 'LABEL') 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(f.name) + ''
+ ' ' + f.chars.toLocaleString() + ' chars';
}).join('');
if (uploadPrompt) uploadPrompt.classList.add('is-hidden');
if (uploadFileInfo) uploadFileInfo.classList.remove('is-hidden');
}
// ── Form submission ───────────────────────────────────────────────────────
if (form) {
form.addEventListener('submit', function (e) {
e.preventDefault();
runSummarize();
});
}
async function runSummarize() {
// Build text: extracted files + textarea
var pastedText = textarea ? textarea.value.trim() : '';
var fileText = _extractedFiles.map(function (f) { return f.text; }).join('\n\n---\n\n');
var combined = [fileText, pastedText].filter(Boolean).join('\n\n---\n\n');
// Doc picker ids
var docIdsEl = document.getElementById('docPickerIds');
var rawDocIds = docIdsEl ? docIdsEl.value.trim() : '';
var docIds = rawDocIds ? rawDocIds.split(',').map(Number).filter(Boolean) : [];
if (!combined && !docIds.length) {
setStatus('Paste text, upload a file, or select a document before running.');
return;
}
var engine = (document.querySelector('input[name="sumEngine"]:checked') || {}).value || 'azure_mini';
var slices = activeSlices();
var payload = {
text: combined,
language: _currentLang,
engine: engine,
slices: slices,
};
if (docIds.length) payload.doc_ids = docIds;
_lastPayload = payload;
setBusy(true);
setStatus('Running…');
showProgress([]);
try {
var resp = await fetch('api/summarize.php', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!resp.ok || !resp.body) {
throw new Error('Server returned ' + resp.status);
}
var reader = resp.body.getReader();
var decoder = new TextDecoder();
var buffer = '';
var steps = [];
while (true) {
var _ref = await reader.read();
var done = _ref.done;
var value = _ref.value;
if (done) break;
buffer += decoder.decode(value, { stream: true });
var lines = buffer.split('\n');
buffer = lines.pop(); // keep incomplete line
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if (!line) continue;
var data;
try { data = JSON.parse(line); } catch (_) { continue; }
if (data.event === 'progress') {
steps.push(data);
showProgress(steps);
setStatus(data.detail || '');
} else if (data.event === 'final') {
renderFinal(data);
if (typeof window.dbnShowSaveResultButton === 'function') {
window.dbnShowSaveResultButton(
'summarize',
_lastPayload || {},
data,
{ model: data.engine || null, latency_ms: data.latency_ms || 0 },
resultsEl
);
}
// Offer deep legal analysis on the summarised text
if (typeof window.dbnInjectLegalAnalysisButton === 'function') {
var sourceText = combined || (_lastPayload && _lastPayload.text) || '';
if (sourceText && sourceText.length >= 80) {
window.dbnInjectLegalAnalysisButton(sourceText, _currentLang, 'summarize', resultsEl);
}
}
if (data.balance != null) {
var credEl = document.getElementById('creditsRemaining');
if (credEl) credEl.textContent = data.balance;
}
} else if (data.event === 'error') {
showError(data.error || 'An error occurred.');
}
}
}
} catch (err) {
showError(err.message || 'Request failed.');
} finally {
setBusy(false);
setStatus('');
}
}
// ── Result rendering ──────────────────────────────────────────────────────
function showProgress(steps) {
if (!resultsEl) return;
var items = steps.map(function (s) {
return ''
+ '' + esc(stepLabel(s.step)) + ''
+ '
' + esc(s.detail || '') + '
';
}).join('');
resultsEl.innerHTML = '' + items
+ 'Working…
';
}
function stepLabel(step) {
var labels = {
text_ready: 'Document prepared',
corpus_search: 'Searching legal corpus',
corpus_done: 'Corpus search done',
generating: 'Generating summary',
};
return labels[step] || step;
}
function renderFinal(data) {
if (!resultsEl) return;
var sections = [];
if (data.what_we_found) {
sections.push(
''
+ 'Summary
'
+ '' + esc(data.what_we_found) + '
'
+ ''
);
}
if (Array.isArray(data.key_facts) && data.key_facts.length) {
sections.push(detailBlock('Key Facts', data.key_facts));
}
if (Array.isArray(data.dates) && data.dates.length) {
sections.push(detailBlock('Dates', data.dates));
}
if (Array.isArray(data.parties) && data.parties.length) {
sections.push(detailBlock('Parties', data.parties));
}
if (Array.isArray(data.legal_references_detected) && data.legal_references_detected.length) {
sections.push(detailBlock('Legal References Detected', data.legal_references_detected));
}
if (Array.isArray(data.what_remains_uncertain) && data.what_remains_uncertain.length) {
sections.push(detailBlock('What Remains Uncertain', data.what_remains_uncertain));
}
if (data.next_practical_step) {
sections.push(
''
+ 'Next Practical Step
'
+ '' + esc(data.next_practical_step) + '
'
+ ''
);
}
if (data.corpus_used) {
sections.push(
''
+ '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.label || '') + ''
+ '
' + esc(item.detail || '') + '
'
+ '';
}).join('');
}
}
function showError(msg) {
if (resultsEl) {
resultsEl.innerHTML = '';
}
setStatus('');
}
function detailBlock(title, items) {
return '' + esc(title) + '
'
+ '
' + items.map(function (v) { return '- ' + esc(String(v)) + '
'; }).join('') + '
'
+ '
';
}
// ── Helpers ───────────────────────────────────────────────────────────────
function setBusy(on) {
if (runBtn) runBtn.disabled = on;
if (runBtn) runBtn.textContent = on ? 'Running…' : 'Summarize';
}
function setStatus(msg) {
if (statusEl) statusEl.textContent = msg;
}
function esc(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
}());