Add timeline sort toggle (doc order / chronological) with CSS
- Wire sortDocOrder / sortChronological click handlers in renderResults() - Add .timeline-sort-bar and .sort-btn styles to tools.css Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+67
-6
@@ -254,6 +254,11 @@ const TIMELINE_I18N = {
|
||||
timelineRunning: 'Building timeline…',
|
||||
timelineReadyTitle: 'Ready',
|
||||
timelineReadyDesc: 'Paste text or upload a file, configure options, then run.',
|
||||
timelineBackground: 'Background events',
|
||||
timelineIncludeBackground: 'Include narrative / background dates',
|
||||
timelineBackgroundHint: 'When checked, historical context dates are included (e.g. "born 30.07.2015", "met around 2011/2012"). Uncheck to extract only operational events and deadlines.',
|
||||
sortDocOrder: 'Document order',
|
||||
sortChronological: 'Chronological',
|
||||
timelineExportCsv: 'Download CSV',
|
||||
},
|
||||
no: {
|
||||
@@ -287,6 +292,11 @@ const TIMELINE_I18N = {
|
||||
timelineRunning: 'Bygger tidslinje…',
|
||||
timelineReadyTitle: 'Klar',
|
||||
timelineReadyDesc: 'Lim inn tekst eller last opp en fil, konfigurer alternativene, og kjør.',
|
||||
timelineBackground: 'Bakgrunnshendelser',
|
||||
timelineIncludeBackground: 'Inkluder narrative / bakgrunnsdatoer',
|
||||
timelineBackgroundHint: 'Når avkrysset inkluderes historiske kontekstdatoer (f.eks. "født 30.07.2015", "møttes rundt 2011/2012"). Fjern haken for å hente kun operasjonelle hendelser og frister.',
|
||||
sortDocOrder: 'Dokumentrekkefølge',
|
||||
sortChronological: 'Kronologisk',
|
||||
timelineExportCsv: 'Last ned CSV',
|
||||
},
|
||||
uk: {
|
||||
@@ -320,6 +330,11 @@ const TIMELINE_I18N = {
|
||||
timelineRunning: 'Будую хронологію…',
|
||||
timelineReadyTitle: 'Готово',
|
||||
timelineReadyDesc: 'Вставте текст або завантажте файл, налаштуйте параметри, запустіть.',
|
||||
timelineBackground: 'Фонові події',
|
||||
timelineIncludeBackground: 'Включити наративні / фонові дати',
|
||||
timelineBackgroundHint: 'Якщо позначено, включаються дати з контексту (напр. "народився 30.07.2015", "зустрілися близько 2011/2012"). Зніміть, щоб витягувати лише операційні події.',
|
||||
sortDocOrder: 'Порядок документа',
|
||||
sortChronological: 'Хронологічний',
|
||||
timelineExportCsv: 'Завантажити CSV',
|
||||
},
|
||||
pl: {
|
||||
@@ -353,11 +368,17 @@ const TIMELINE_I18N = {
|
||||
timelineRunning: 'Tworzę oś czasu…',
|
||||
timelineReadyTitle: 'Gotowe',
|
||||
timelineReadyDesc: 'Wklej tekst lub wgraj plik, skonfiguruj opcje, uruchom.',
|
||||
timelineBackground: 'Zdarzenia w tle',
|
||||
timelineIncludeBackground: 'Uwzględnij daty narracyjne / kontekstowe',
|
||||
timelineBackgroundHint: 'Gdy zaznaczone, daty z kontekstu są uwzględniane (np. "urodzony 30.07.2015", "poznali się około 2011/2012"). Odznacz, aby wyodrębniać tylko zdarzenia operacyjne.',
|
||||
sortDocOrder: 'Kolejność dokumentu',
|
||||
sortChronological: 'Chronologicznie',
|
||||
timelineExportCsv: 'Pobierz CSV',
|
||||
},
|
||||
};
|
||||
|
||||
let lastTimelineEvents = [];
|
||||
let lastTimelineEventsOriginal = [];
|
||||
let audioQueue = []; // [{file, status: 'pending'|'processing'|'done'|'error', result}]
|
||||
let lastTranscriptData = null;
|
||||
let lastRedactedText = null;
|
||||
@@ -762,6 +783,21 @@ function currentIncludeRelative() {
|
||||
return document.getElementById('includeRelativeCheck')?.checked ?? true;
|
||||
}
|
||||
|
||||
function currentIncludeBackground() {
|
||||
return document.getElementById('includeBackgroundCheck')?.checked ?? true;
|
||||
}
|
||||
|
||||
function sortChronological(events) {
|
||||
const isIso = (d) => /^\d{4}-\d{2}/.test(d);
|
||||
return [...events].sort((a, b) => {
|
||||
const da = a.date || '', db = b.date || '';
|
||||
if (isIso(da) && isIso(db)) return da.localeCompare(db);
|
||||
if (isIso(da)) return -1;
|
||||
if (isIso(db)) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
function setupTimelineControls() {
|
||||
const switcher = document.getElementById('timelineLangSwitcher');
|
||||
if (!switcher) return;
|
||||
@@ -1040,10 +1076,11 @@ async function runTool(event) {
|
||||
payload.redact_types = currentRedactTypes();
|
||||
}
|
||||
if (state.activeTool === 'timeline') {
|
||||
payload.engine = currentTimelineEngine();
|
||||
payload.focus = currentTimelineFocus();
|
||||
payload.confidence_filter = currentConfidenceFilter();
|
||||
payload.include_relative = currentIncludeRelative();
|
||||
payload.engine = currentTimelineEngine();
|
||||
payload.focus = currentTimelineFocus();
|
||||
payload.confidence_filter = currentConfidenceFilter();
|
||||
payload.include_relative = currentIncludeRelative();
|
||||
payload.include_background = currentIncludeBackground();
|
||||
}
|
||||
|
||||
setBusy(true);
|
||||
@@ -1288,6 +1325,23 @@ function renderResults(data) {
|
||||
sections.push(renderFeedbackWidget());
|
||||
els.results.innerHTML = sections.join('');
|
||||
setupFeedbackWidget(data.tool || state.activeTool);
|
||||
|
||||
const sortDoc = document.getElementById('sortDocOrder');
|
||||
const sortChr = document.getElementById('sortChronological');
|
||||
if (sortDoc && sortChr) {
|
||||
sortDoc.addEventListener('click', () => {
|
||||
lastTimelineEvents = [...lastTimelineEventsOriginal];
|
||||
document.getElementById('timelineListContainer').innerHTML = renderTimeline(lastTimelineEvents);
|
||||
sortDoc.classList.add('is-active');
|
||||
sortChr.classList.remove('is-active');
|
||||
});
|
||||
sortChr.addEventListener('click', () => {
|
||||
lastTimelineEvents = sortChronological(lastTimelineEventsOriginal);
|
||||
document.getElementById('timelineListContainer').innerHTML = renderTimeline(lastTimelineEvents);
|
||||
sortChr.classList.add('is-active');
|
||||
sortDoc.classList.remove('is-active');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function renderMainFinding(data) {
|
||||
@@ -1305,12 +1359,19 @@ function renderMainFinding(data) {
|
||||
return `<pre class="redacted-output">${escapeHtml(lastRedactedText)}</pre>${renderEntityCounts(data.entity_counts)}${dlRow}`;
|
||||
}
|
||||
if (data.tool === 'timeline') {
|
||||
lastTimelineEvents = data.events || [];
|
||||
lastTimelineEventsOriginal = data.events || [];
|
||||
lastTimelineEvents = [...lastTimelineEventsOriginal];
|
||||
const csvLabel = currentTimelineT('timelineExportCsv') || 'Download CSV';
|
||||
const csvBtn = lastTimelineEvents.length
|
||||
? `<div class="timeline-export"><button type="button" id="exportCsvBtn" class="export-csv-btn">${escapeHtml(csvLabel)}</button></div>`
|
||||
: '';
|
||||
return `<p>${escapeHtml(data.what_we_found || '')}</p>${renderTimeline(lastTimelineEvents)}${csvBtn}`;
|
||||
const sortBar = lastTimelineEvents.length > 1 ? `
|
||||
<div class="timeline-sort-bar">
|
||||
<span class="sort-label">Sort:</span>
|
||||
<button type="button" class="sort-btn is-active" id="sortDocOrder">${escapeHtml(currentTimelineT('sortDocOrder') || 'Document order')}</button>
|
||||
<button type="button" class="sort-btn" id="sortChronological">${escapeHtml(currentTimelineT('sortChronological') || 'Chronological')}</button>
|
||||
</div>` : '';
|
||||
return `<p>${escapeHtml(data.what_we_found || '')}</p>${sortBar}<div id="timelineListContainer">${renderTimeline(lastTimelineEvents)}</div>${csvBtn}`;
|
||||
}
|
||||
if (data.tool === 'summarize') {
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user