Quellenübersicht: Lagebild-Quellennummer [N] statt fortlaufender Nummer

Statt einer eigenen Nummerierung (1., 2., ...) wird jetzt die echte
Lagebild-Quellennummer im Format [N] angezeigt — also exakt das, was im
Lagebild-Text als Zitat erscheint. Match per exakter source_url, mit
Quellen-Name als Fallback.

Artikel ohne Match (nicht im Lagebild zitiert) bekommen einen dezenten
Strich "—" mit Tooltip "Nicht im Lagebild zitiert", damit sichtbar ist
welche Belege Claude überhaupt verwendet hat und welche nicht.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
2026-05-01 18:04:52 +02:00
Ursprung 51615cae62
Commit ad5b723d79
3 geänderte Dateien mit 37 neuen und 7 gelöschten Zeilen

Datei anzeigen

@@ -1776,8 +1776,13 @@ a.dev-source-pill:hover {
font-size: 11px; font-size: 11px;
font-weight: 700; font-weight: 700;
color: var(--accent); color: var(--accent);
min-width: 32px; min-width: 36px;
text-align: right; text-align: right;
white-space: nowrap;
}
.source-overview-detail-num--none {
color: var(--text-disabled);
font-weight: 400;
} }
.source-overview-detail-date { .source-overview-detail-date {
font-family: var(--font-mono); font-family: var(--font-mono);

Datei anzeigen

@@ -13,7 +13,7 @@
<link rel="stylesheet" href="/static/vendor/leaflet.css"> <link rel="stylesheet" href="/static/vendor/leaflet.css">
<link rel="stylesheet" href="/static/vendor/MarkerCluster.css"> <link rel="stylesheet" href="/static/vendor/MarkerCluster.css">
<link rel="stylesheet" href="/static/vendor/MarkerCluster.Default.css"> <link rel="stylesheet" href="/static/vendor/MarkerCluster.Default.css">
<link rel="stylesheet" href="/static/css/style.css?v=20260501g"> <link rel="stylesheet" href="/static/css/style.css?v=20260501h">
<style> <style>
/* Export Modal Radio */ /* Export Modal Radio */
.export-radio { display:flex; align-items:center; gap:10px; padding:8px 12px; cursor:pointer; border-radius:var(--radius-sm); transition:background 0.15s; border:1px solid transparent; margin-bottom:4px; } .export-radio { display:flex; align-items:center; gap:10px; padding:8px 12px; cursor:pointer; border-radius:var(--radius-sm); transition:background 0.15s; border:1px solid transparent; margin-bottom:4px; }
@@ -647,7 +647,7 @@
<script src="/static/js/components.js?v=20260427a"></script> <script src="/static/js/components.js?v=20260427a"></script>
<script src="/static/js/layout.js?v=20260316b"></script> <script src="/static/js/layout.js?v=20260316b"></script>
<script src="/static/js/pipeline.js?v=20260501a"></script> <script src="/static/js/pipeline.js?v=20260501a"></script>
<script src="/static/js/app.js?v=20260501g"></script> <script src="/static/js/app.js?v=20260501h"></script>
<script src="/static/js/cluster-data.js?v=20260322f"></script> <script src="/static/js/cluster-data.js?v=20260322f"></script>
<script src="/static/js/tutorial.js?v=20260316z"></script> <script src="/static/js/tutorial.js?v=20260316z"></script>
<script src="/static/js/chat.js?v=20260422a"></script> <script src="/static/js/chat.js?v=20260422a"></script>

Datei anzeigen

@@ -899,6 +899,28 @@ const App = {
return tb - ta; return tb - ta;
}); });
// Lagebild-Quellennummer pro Artikel ermitteln (matcht Artikel zu sources_json)
const normalize = (s) => (s || '').toLowerCase().replace(/^(der|die|das)\s+/, '').replace(/\s+/g, ' ').trim();
const sourcesList = this._currentSources || [];
const urlToNr = new Map();
sourcesList.forEach(s => {
if (s.url && s.nr != null) urlToNr.set(String(s.url).trim(), s.nr);
});
const findNr = (a) => {
// 1) Exakter URL-Match
if (a.source_url) {
const exact = urlToNr.get(String(a.source_url).trim());
if (exact != null) return exact;
}
// 2) Fallback: Match via Quellen-Namen (kann mehrfach treffen, nimm erstes)
if (a.source) {
const target = normalize(a.source);
const hit = sourcesList.find(s => s.nr != null && normalize(s.name) === target);
if (hit) return hit.nr;
}
return null;
};
const detail = document.createElement('div'); const detail = document.createElement('div');
detail.className = 'source-overview-detail'; detail.className = 'source-overview-detail';
if (articles.length === 0) { if (articles.length === 0) {
@@ -914,20 +936,23 @@ const App = {
+ d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); + d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE });
} catch (e) { return '—'; } } catch (e) { return '—'; }
}; };
const items = articles.map((a, i) => { const items = articles.map(a => {
const num = i + 1; const nr = findNr(a);
const numHtml = nr != null
? `<span class="source-overview-detail-num">[${UI.escape(String(nr))}]</span>`
: `<span class="source-overview-detail-num source-overview-detail-num--none" title="Nicht im Lagebild zitiert">—</span>`;
const dateStr = fmtDate(getDate(a)); const dateStr = fmtDate(getDate(a));
const headline = UI.escape(a.headline_de || a.headline || '(ohne Titel)'); const headline = UI.escape(a.headline_de || a.headline || '(ohne Titel)');
const inner = a.source_url const inner = a.source_url
? `<a href="${UI.escape(a.source_url)}" target="_blank" rel="noopener">${headline}</a>` ? `<a href="${UI.escape(a.source_url)}" target="_blank" rel="noopener">${headline}</a>`
: headline; : headline;
return `<li> return `<li>
<span class="source-overview-detail-num">${num}.</span> ${numHtml}
<span class="source-overview-detail-date">${UI.escape(dateStr)}</span> <span class="source-overview-detail-date">${UI.escape(dateStr)}</span>
<span class="source-overview-detail-headline">${inner}</span> <span class="source-overview-detail-headline">${inner}</span>
</li>`; </li>`;
}).join(''); }).join('');
detail.innerHTML = `<ol class="source-overview-detail-list">${items}</ol>`; detail.innerHTML = `<ul class="source-overview-detail-list">${items}</ul>`;
} }
el.insertAdjacentElement('afterend', detail); el.insertAdjacentElement('afterend', detail);
}, },