Timeline: document upload, upgraded prompt, CSV export, date_type badge

This commit is contained in:
2026-05-13 08:10:40 +02:00
parent 634a4fa154
commit bddafea049
4 changed files with 84 additions and 15 deletions
+33 -7
View File
@@ -3,6 +3,8 @@ const state = {
authenticated: Boolean(window.DBN_TOOLS_AUTHENTICATED),
};
let lastTimelineEvents = [];
const tools = {
ask: {
kind: 'Source-grounded Legal Ask',
@@ -99,6 +101,9 @@ document.addEventListener('DOMContentLoaded', () => {
els.healthButton.addEventListener('click', checkHealth);
setupUpload();
setupAliases();
els.results.addEventListener('click', (e) => {
if (e.target.closest('#exportCsvBtn')) exportTimelineCSV(lastTimelineEvents);
});
setTool(state.activeTool);
if (state.authenticated) {
@@ -125,7 +130,7 @@ function setTool(toolName) {
els.input.placeholder = tool.placeholder;
els.languageControl.classList.toggle('is-hidden', !tool.usesLanguage);
els.redactionControl.classList.toggle('is-hidden', toolName !== 'redact');
els.uploadZone.classList.toggle('is-hidden', toolName !== 'redact');
els.uploadZone.classList.toggle('is-hidden', toolName !== 'redact' && toolName !== 'timeline');
els.aliasSection.classList.toggle('is-hidden', toolName !== 'redact');
resetUpload();
resetAliases();
@@ -421,7 +426,11 @@ function renderMainFinding(data) {
return `<pre class="redacted-output">${escapeHtml(data.redacted_text || '')}</pre>${renderEntityCounts(data.entity_counts)}`;
}
if (data.tool === 'timeline') {
return `<p>${escapeHtml(data.what_we_found || '')}</p>${renderTimeline(data.events || [])}`;
lastTimelineEvents = data.events || [];
const csvBtn = lastTimelineEvents.length
? `<div class="timeline-export"><button type="button" id="exportCsvBtn" class="export-csv-btn">Download CSV</button></div>`
: '';
return `<p>${escapeHtml(data.what_we_found || '')}</p>${renderTimeline(lastTimelineEvents)}${csvBtn}`;
}
if (data.tool === 'summarize') {
return [
@@ -477,16 +486,33 @@ function renderTimeline(events) {
if (!events.length) {
return '<p>No events were identified.</p>';
}
return `<ol class="timeline-list">${events.map((event) => `
return `<ol class="timeline-list">${events.map((ev) => `
<li>
<strong>${escapeHtml(event.date || 'unknown')}</strong>
<span>${escapeHtml(event.actor || 'unknown actor')}</span>
<p>${escapeHtml(event.event || '')}</p>
${event.source_excerpt ? `<small>${escapeHtml(event.source_excerpt)}</small>` : ''}
<strong>${escapeHtml(ev.date || 'unknown')}</strong>
${ev.date_type ? `<span class="date-type-badge">${escapeHtml(ev.date_type)}</span>` : ''}
<span>${escapeHtml(ev.actor || 'unknown actor')}</span>
<p>${escapeHtml(ev.event || '')}</p>
${ev.source_excerpt ? `<small>${escapeHtml(ev.source_excerpt)}</small>` : ''}
</li>
`).join('')}</ol>`;
}
function exportTimelineCSV(events) {
const header = ['Date', 'Date Type', 'Actor', 'Event', 'Source Excerpt', 'Confidence'];
const rows = events.map((ev) => [
ev.date || '', ev.date_type || '', ev.actor || '',
ev.event || '', ev.source_excerpt || '', ev.confidence || '',
]);
const csv = [header, ...rows]
.map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(','))
.join('\n');
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'), { href: url, download: 'timeline.csv' });
a.click();
URL.revokeObjectURL(url);
}
function renderEntityCounts(counts = {}) {
const entries = Object.entries(counts).filter(([, count]) => Number(count) > 0);
if (!entries.length) {