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:
@@ -1122,6 +1122,31 @@ p {
|
|||||||
margin: 0.35rem 0 0;
|
margin: 0.35rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timeline-sort-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.sort-label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
.sort-btn {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.22rem 0.7rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
||||||
|
}
|
||||||
|
.sort-btn:hover { background: var(--soft-teal); color: var(--teal-dark); border-color: var(--teal); }
|
||||||
|
.sort-btn.is-active { background: var(--teal); color: #fff; border-color: var(--teal); }
|
||||||
|
|
||||||
.timeline-export { margin-top: 1rem; }
|
.timeline-export { margin-top: 1rem; }
|
||||||
|
|
||||||
.export-csv-btn {
|
.export-csv-btn {
|
||||||
|
|||||||
+63
-2
@@ -254,6 +254,11 @@ const TIMELINE_I18N = {
|
|||||||
timelineRunning: 'Building timeline…',
|
timelineRunning: 'Building timeline…',
|
||||||
timelineReadyTitle: 'Ready',
|
timelineReadyTitle: 'Ready',
|
||||||
timelineReadyDesc: 'Paste text or upload a file, configure options, then run.',
|
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',
|
timelineExportCsv: 'Download CSV',
|
||||||
},
|
},
|
||||||
no: {
|
no: {
|
||||||
@@ -287,6 +292,11 @@ const TIMELINE_I18N = {
|
|||||||
timelineRunning: 'Bygger tidslinje…',
|
timelineRunning: 'Bygger tidslinje…',
|
||||||
timelineReadyTitle: 'Klar',
|
timelineReadyTitle: 'Klar',
|
||||||
timelineReadyDesc: 'Lim inn tekst eller last opp en fil, konfigurer alternativene, og kjør.',
|
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',
|
timelineExportCsv: 'Last ned CSV',
|
||||||
},
|
},
|
||||||
uk: {
|
uk: {
|
||||||
@@ -320,6 +330,11 @@ const TIMELINE_I18N = {
|
|||||||
timelineRunning: 'Будую хронологію…',
|
timelineRunning: 'Будую хронологію…',
|
||||||
timelineReadyTitle: 'Готово',
|
timelineReadyTitle: 'Готово',
|
||||||
timelineReadyDesc: 'Вставте текст або завантажте файл, налаштуйте параметри, запустіть.',
|
timelineReadyDesc: 'Вставте текст або завантажте файл, налаштуйте параметри, запустіть.',
|
||||||
|
timelineBackground: 'Фонові події',
|
||||||
|
timelineIncludeBackground: 'Включити наративні / фонові дати',
|
||||||
|
timelineBackgroundHint: 'Якщо позначено, включаються дати з контексту (напр. "народився 30.07.2015", "зустрілися близько 2011/2012"). Зніміть, щоб витягувати лише операційні події.',
|
||||||
|
sortDocOrder: 'Порядок документа',
|
||||||
|
sortChronological: 'Хронологічний',
|
||||||
timelineExportCsv: 'Завантажити CSV',
|
timelineExportCsv: 'Завантажити CSV',
|
||||||
},
|
},
|
||||||
pl: {
|
pl: {
|
||||||
@@ -353,11 +368,17 @@ const TIMELINE_I18N = {
|
|||||||
timelineRunning: 'Tworzę oś czasu…',
|
timelineRunning: 'Tworzę oś czasu…',
|
||||||
timelineReadyTitle: 'Gotowe',
|
timelineReadyTitle: 'Gotowe',
|
||||||
timelineReadyDesc: 'Wklej tekst lub wgraj plik, skonfiguruj opcje, uruchom.',
|
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',
|
timelineExportCsv: 'Pobierz CSV',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastTimelineEvents = [];
|
let lastTimelineEvents = [];
|
||||||
|
let lastTimelineEventsOriginal = [];
|
||||||
let audioQueue = []; // [{file, status: 'pending'|'processing'|'done'|'error', result}]
|
let audioQueue = []; // [{file, status: 'pending'|'processing'|'done'|'error', result}]
|
||||||
let lastTranscriptData = null;
|
let lastTranscriptData = null;
|
||||||
let lastRedactedText = null;
|
let lastRedactedText = null;
|
||||||
@@ -762,6 +783,21 @@ function currentIncludeRelative() {
|
|||||||
return document.getElementById('includeRelativeCheck')?.checked ?? true;
|
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() {
|
function setupTimelineControls() {
|
||||||
const switcher = document.getElementById('timelineLangSwitcher');
|
const switcher = document.getElementById('timelineLangSwitcher');
|
||||||
if (!switcher) return;
|
if (!switcher) return;
|
||||||
@@ -1044,6 +1080,7 @@ async function runTool(event) {
|
|||||||
payload.focus = currentTimelineFocus();
|
payload.focus = currentTimelineFocus();
|
||||||
payload.confidence_filter = currentConfidenceFilter();
|
payload.confidence_filter = currentConfidenceFilter();
|
||||||
payload.include_relative = currentIncludeRelative();
|
payload.include_relative = currentIncludeRelative();
|
||||||
|
payload.include_background = currentIncludeBackground();
|
||||||
}
|
}
|
||||||
|
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
@@ -1288,6 +1325,23 @@ function renderResults(data) {
|
|||||||
sections.push(renderFeedbackWidget());
|
sections.push(renderFeedbackWidget());
|
||||||
els.results.innerHTML = sections.join('');
|
els.results.innerHTML = sections.join('');
|
||||||
setupFeedbackWidget(data.tool || state.activeTool);
|
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) {
|
function renderMainFinding(data) {
|
||||||
@@ -1305,12 +1359,19 @@ function renderMainFinding(data) {
|
|||||||
return `<pre class="redacted-output">${escapeHtml(lastRedactedText)}</pre>${renderEntityCounts(data.entity_counts)}${dlRow}`;
|
return `<pre class="redacted-output">${escapeHtml(lastRedactedText)}</pre>${renderEntityCounts(data.entity_counts)}${dlRow}`;
|
||||||
}
|
}
|
||||||
if (data.tool === 'timeline') {
|
if (data.tool === 'timeline') {
|
||||||
lastTimelineEvents = data.events || [];
|
lastTimelineEventsOriginal = data.events || [];
|
||||||
|
lastTimelineEvents = [...lastTimelineEventsOriginal];
|
||||||
const csvLabel = currentTimelineT('timelineExportCsv') || 'Download CSV';
|
const csvLabel = currentTimelineT('timelineExportCsv') || 'Download CSV';
|
||||||
const csvBtn = lastTimelineEvents.length
|
const csvBtn = lastTimelineEvents.length
|
||||||
? `<div class="timeline-export"><button type="button" id="exportCsvBtn" class="export-csv-btn">${escapeHtml(csvLabel)}</button></div>`
|
? `<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') {
|
if (data.tool === 'summarize') {
|
||||||
return [
|
return [
|
||||||
|
|||||||
Reference in New Issue
Block a user