Lagebild-Seite: Tab Neueste Entwicklungen / Zusammenfassung

Live-Lagen (iran-konflikt, cyberangriffe) bekommen einen neuen ersten Tab
Neueste Entwicklungen mit Bullet-Cards, klickbaren Quellen-Pills und
Zeitstempel. Recherche-Lagen (deepfakes) bekommen an selber Stelle einen
Tab Zusammenfassung, der den Zusammenfassung-Abschnitt aus dem Markdown
extrahiert und mit Citation-Links rendert.

lagebild.js: renderUeberblick, renderLatestDevelopmentsHtml,
extractZusammenfassung ergaenzt. i18n-Keys tabUeberblick/Research.
Lang-Toggle aktualisiert Tab-Label und h2.

Vorschau-Karten zeigen wieder den Lagebild-Text fuer alle Lagen
(renderLatestDevelopments-Calls aus loadLiveData entfernt).
Dieser Commit ist enthalten in:
Claude Code
2026-04-19 00:19:14 +02:00
Ursprung 8b8072efe7
Commit c3bae27837
7 geänderte Dateien mit 415 neuen und 7 gelöschten Zeilen

Datei anzeigen

@@ -76,7 +76,8 @@
<div class="timeline-dropdown" id="timeline-dropdown"></div>
</div>
<div class="tab-nav" id="tab-nav">
<button class="tab-btn active" data-tab="lagebild">Lagebild</button>
<button class="tab-btn active" data-tab="ueberblick">Neueste Entwicklungen</button>
<button class="tab-btn" data-tab="lagebild">Lagebild</button>
<button class="tab-btn" data-tab="karte">Karte</button>
<button class="tab-btn" data-tab="faktenchecks">Faktenchecks <span class="tab-badge" id="tab-badge-faktenchecks"></span></button>
<button class="tab-btn" data-tab="quellen">Quellen <span class="tab-badge" id="tab-badge-quellen"></span></button>
@@ -87,8 +88,26 @@
<!-- Main Content -->
<main class="lagebild-main">
<div class="container">
<!-- Tab: Ueberblick (Neueste Entwicklungen / Zusammenfassung) -->
<div class="tab-panel active" id="panel-ueberblick">
<p class="data-source-note">Daten bereitgestellt durch AegisSight Monitor</p>
<section class="content-card">
<div class="card-header">
<h2 id="ueberblick-title">Neueste Entwicklungen</h2>
<span class="card-timestamp" id="ueberblick-timestamp"></span>
</div>
<div class="card-body" id="ueberblick-content">
<div class="loading-skeleton">
<div class="skeleton-line"></div>
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
</div>
</section>
</div>
<!-- Tab: Lagebild -->
<div class="tab-panel active" id="panel-lagebild">
<div class="tab-panel" id="panel-lagebild">
<p class="data-source-note">Daten bereitgestellt durch AegisSight Monitor</p>
<section class="content-card">
<div class="card-header">

Datei anzeigen

@@ -1567,3 +1567,69 @@ a.source-detail-article-title:hover {
color: #0A1832;
font-weight: 600;
}
/* === Neueste Entwicklungen / Ueberblick-Tab === */
.dev-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.dev-bullet {
background: rgba(30, 45, 69, 0.5);
border-left: 3px solid #C8A851;
border-radius: 4px;
padding: 10px 14px;
}
.dev-bullet-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
margin-bottom: 6px;
flex-wrap: wrap;
}
.dev-sources {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
min-width: 0;
}
.dev-source-pill {
display: inline-block;
padding: 3px 10px;
background: rgba(200, 168, 81, 0.18);
color: #E8ECF4;
border-radius: 3px;
font-size: 0.78rem;
font-weight: 500;
text-decoration: none;
line-height: 1.5;
max-width: 240px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: background 0.15s;
}
a.dev-source-pill:hover {
background: rgba(200, 168, 81, 0.35);
text-decoration: none;
color: #E8ECF4;
}
.dev-time {
color: #8896AB;
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
white-space: nowrap;
flex-shrink: 0;
}
.dev-body {
font-size: 0.95rem;
line-height: 1.5;
color: #E8ECF4;
}
.empty-hint {
color: #8896AB;
font-style: italic;
padding: 16px 0;
}

Datei anzeigen

@@ -19,6 +19,7 @@ var Lagebild = {
de: {
hero: "LAGEBILD", heroResearch: "RECHERCHE",
tabBriefing: "Lagebild", tabBriefingResearch: "Recherche",
tabUeberblick: "Neueste Entwicklungen", tabUeberblickResearch: "Zusammenfassung",
tabMap: "Karte", tabFactchecks: "Faktenchecks", tabSources: "Quellen",
statArticles: "Artikel", statSources: "Quellen", statFactchecks: "Faktenchecks",
dataSource: "Daten bereitgestellt durch AegisSight Monitor",
@@ -44,6 +45,7 @@ var Lagebild = {
en: {
hero: "SITUATION REPORT", heroResearch: "RESEARCH BRIEFING",
tabBriefing: "Briefing", tabBriefingResearch: "Research",
tabUeberblick: "Latest Developments", tabUeberblickResearch: "Summary",
tabMap: "Map", tabFactchecks: "Fact Checks", tabSources: "Sources",
statArticles: "Articles", statSources: "Sources", statFactchecks: "Fact Checks",
dataSource: "Data provided by AegisSight Monitor",
@@ -133,6 +135,125 @@ var Lagebild = {
this.renderTimeline();
this.renderTabBadges();
this.renderCurrentView();
this.renderUeberblick();
},
/* ===== TAB: UEBERBLICK (Neueste Entwicklungen / Zusammenfassung) ===== */
renderUeberblick: function() {
var inc = (this.data && this.data.incident) || {};
var el = document.getElementById('ueberblick-content');
var tsEl = document.getElementById('ueberblick-timestamp');
if (!el) return;
if (tsEl) tsEl.textContent = this.fmtDT(this.currentView && this.currentView.updated_at);
if (inc.type === 'adhoc') {
var dev = inc.latest_developments || '';
var sources = (this.currentView && this.currentView.sources_json) || [];
var html = this.renderLatestDevelopmentsHtml(dev, sources);
el.innerHTML = html || '<p class="empty-hint">Noch keine Entwicklungen erfasst.</p>';
} else {
var md = (this.currentView && this.currentView.summary) || '';
var zf = this.extractZusammenfassung(md);
if (!zf) {
el.innerHTML = '<p class="empty-hint">Keine Zusammenfassung verf&uuml;gbar.</p>';
return;
}
var body = this.mdToHtml(this.fixUmlauts(zf));
var srcMap = {};
var sources = (this.currentView && this.currentView.sources_json) || [];
for (var i = 0; i < sources.length; i++) srcMap[String(sources[i].nr)] = sources[i];
var self = this;
body = body.replace(/\[(\d+[a-z]?)\]/g, function(match, nr) {
var src = srcMap[nr];
if (src && src.url) {
return '<a class="citation-ref" href="' + self.esc(src.url) + '" target="_blank" rel="noopener" title="' + self.esc(src.name || '') + '">[' + nr + ']</a>';
}
return '<a class="citation-ref" title="Quelle ' + nr + '">[' + nr + ']</a>';
});
el.innerHTML = body;
}
},
extractZusammenfassung: function(md) {
if (!md) return '';
var sections = md.split(/^## /m);
for (var i = 0; i < sections.length; i++) {
var s = sections[i];
if (/^zusammenfassung/i.test(s.trim())) {
var next = s.split(/\n## /)[0];
return next.trim();
}
}
return '';
},
renderLatestDevelopmentsHtml: function(text, sources) {
if (!text) return '';
sources = Array.isArray(sources) ? sources : [];
var self = this;
var lines = text.split('\n').map(function(l){return l.trim();})
.filter(function(l){return l && (l.charAt(0) === '-' || l.charAt(0) === '[');});
if (!lines.length) return '';
var bulletRe = /^(?:-\s*)?\[\s*(\d{1,2})\.(\d{1,2})\.?(?:\d{2,4})?\s+(\d{1,2}:\d{2})\s*\]\s*(.+?)\s*$/;
var trailingRe = /\s*\{([^{}]+)\}\s*\.?\s*$/;
var citationRe = /\[(\d+[a-z]?)\]/g;
var junkRe = /^(unbekannt|unknown|n\/?a|keine|keine quelle|tba)$/i;
function normName(s) { return String(s||'').toLowerCase().replace(/^(der|die|das)\s+/,'').replace(/\s+/g,' ').trim(); }
function lookupByName(name) {
var n = normName(name); if (!n) return null;
var exact = sources.find(function(s){ return normName(s.name) === n; });
if (exact) return exact;
return sources.find(function(s){ var sn=normName(s.name); return sn && (sn.indexOf(n)!==-1 || n.indexOf(sn)!==-1); }) || null;
}
function buildPill(src, name) {
var disp = (src && src.name) || name;
var e = self.esc(disp);
if (src && src.url) return '<a href="'+self.esc(src.url)+'" target="_blank" rel="noopener" class="dev-source-pill" title="'+e+'">'+e+'</a>';
return '<span class="dev-source-pill" title="'+e+'">'+e+'</span>';
}
var cards = [];
for (var i = 0; i < lines.length; i++) {
var m = bulletRe.exec(lines[i]); if (!m) continue;
var date = String(m[1]).padStart(2,'0') + '.' + String(m[2]).padStart(2,'0') + '.';
var time = m[3]; var body = m[4];
var pills = '';
var t = trailingRe.exec(body);
if (t) {
body = body.replace(trailingRe, '').trim();
var names = t[1].split(',').map(function(n){return n.trim();}).filter(Boolean);
var seen = {};
pills = names.map(function(n){
if (junkRe.test(n)) return '';
var k = normName(n); if (seen[k]) return ''; seen[k] = true;
return buildPill(lookupByName(n), n);
}).filter(Boolean).join('');
}
if (!pills) {
var nums = []; var cm;
while ((cm = citationRe.exec(body)) !== null) { if (nums.indexOf(cm[1]) === -1) nums.push(cm[1]); }
citationRe.lastIndex = 0;
if (nums.length) {
body = body.replace(citationRe, '').replace(/\s+/g, ' ').trim();
pills = nums.map(function(num){
var src = sources.find(function(s){ return String(s.nr) === num || Number(s.nr) === Number(num); });
return src ? buildPill(src, src.name) : '';
}).filter(Boolean).join('');
}
}
var head = '<div class="dev-bullet-head">'
+ '<span class="dev-sources">' + pills + '</span>'
+ '<span class="dev-time">' + self.esc(time) + ' \u00b7 ' + self.esc(date) + '</span>'
+ '</div>';
cards.push('<div class="dev-bullet">' + head + '<div class="dev-body">' + self.esc(body) + '</div></div>');
}
return cards.length ? '<div class="dev-list">' + cards.join('') + '</div>' : '';
},
/* ===== SCROLL PROGRESS BAR ===== */
@@ -1099,10 +1220,14 @@ var Lagebild = {
for (var i = 0; i < tabBtns.length; i++) {
var tab = tabBtns[i].getAttribute('data-tab');
if (tab === 'lagebild') tabBtns[i].childNodes[0].textContent = isResearch ? this.t('tabBriefingResearch') : this.t('tabBriefing');
else if (tab === 'ueberblick') tabBtns[i].childNodes[0].textContent = isResearch ? this.t('tabUeberblickResearch') : this.t('tabUeberblick');
else if (tab === 'karte') tabBtns[i].childNodes[0].textContent = this.t('tabMap');
else if (tab === 'faktenchecks') tabBtns[i].childNodes[0].textContent = this.t('tabFactchecks') + ' ';
else if (tab === 'quellen') tabBtns[i].childNodes[0].textContent = this.t('tabSources') + ' ';
}
// Update Ueberblick H2 title
var ueberblickH2 = document.getElementById('ueberblick-title');
if (ueberblickH2) ueberblickH2.textContent = isResearch ? this.t('tabUeberblickResearch') : this.t('tabUeberblick');
// Update data source note
var dsNote = document.querySelector('.data-source-note');
if (dsNote) dsNote.textContent = this.t('dataSource');