diff --git a/src/static/css/style.css b/src/static/css/style.css index dbdd55c..40b1028 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -1064,6 +1064,103 @@ a:hover { white-space: nowrap; } +/* === Neueste Entwicklungen (Live-Monitoring) === */ +.dev-list { + display: flex; + flex-direction: column; + gap: var(--sp-sm); + white-space: normal; +} + +.dev-bullet { + background: var(--bg-elevated); + border-left: 3px solid var(--accent); + border-radius: var(--radius); + padding: var(--sp-md) var(--sp-lg); +} + +.dev-bullet-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--sp-md); + margin-bottom: var(--sp-xs); + flex-wrap: wrap; +} + +.dev-sources { + display: inline-flex; + flex-wrap: wrap; + gap: var(--sp-xs); + align-items: center; + min-width: 0; +} + +.dev-sources-empty { + color: var(--text-disabled); + font-size: 11px; + font-style: italic; +} + +.dev-source-pill { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + background: var(--tint-accent); + color: var(--text-primary); + border-radius: 3px; + font-size: 11px; + font-weight: 500; + text-decoration: none; + line-height: 1.5; + transition: background 0.15s; + max-width: 220px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +a.dev-source-pill:hover { + background: var(--tint-accent-strong); + text-decoration: none; + color: var(--text-primary); +} + +.dev-bias { + font-size: 9px; + padding: 1px 4px; + border-radius: 2px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.3px; + line-height: 1.4; +} + +.dev-bias-pro-ru { + background: var(--tint-error); + color: var(--error); +} + +.dev-bias-staatsnah { + background: var(--tint-warning); + color: var(--warning); +} + +.dev-time { + color: var(--text-tertiary); + font-size: 11px; + font-variant-numeric: tabular-nums; + white-space: nowrap; + flex-shrink: 0; +} + +.dev-body { + font-size: 14px; + line-height: 1.5; + color: var(--text-primary); +} + /* === Faktencheck Card === */ .factcheck-list { display: flex; diff --git a/src/static/js/app.js b/src/static/js/app.js index ff38356..95c9344 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -878,7 +878,7 @@ const App = { if (zusammenfassungCard) zusammenfassungCard.style.display = ''; const devText = (incident.latest_developments || '').trim(); if (devText) { - if (zusammenfassungText) zusammenfassungText.innerHTML = UI.renderZusammenfassung(devText, incident.sources_json); + if (zusammenfassungText) zusammenfassungText.innerHTML = UI.renderLatestDevelopments(devText, incident.sources_json); } else if (zusammenfassungText) { zusammenfassungText.innerHTML = 'Noch keine Entwicklungen erfasst. Wird beim n\u00e4chsten Refresh generiert.'; } diff --git a/src/static/js/components.js b/src/static/js/components.js index 1c55304..375eb97 100644 --- a/src/static/js/components.js +++ b/src/static/js/components.js @@ -744,6 +744,92 @@ const UI = { return html; }, + /** + * Rendert "Neueste Entwicklungen" für Live-Monitoring (adhoc). + * Erwartet Bullets im Format "- [DD.MM. HH:MM] Text [N][M]". + * Erzeugt Karten mit sichtbaren Quellen-Pills + dezentem Zeitstempel. + */ + renderLatestDevelopments(text, sourcesJson) { + if (!text) return 'Noch keine Entwicklungen erfasst.'; + let sources = []; + try { sources = JSON.parse(sourcesJson || '[]'); } catch(e) {} + + const bulletLines = text.split("\n").map(l => l.trim()).filter(l => l.startsWith("- ")); + if (bulletLines.length === 0) { + // Fallback: kein Bullet-Format erkannt, alten Render verwenden + return this.renderZusammenfassung(text, sourcesJson); + } + + const bulletRe = /^-\s*\[(\d{1,2}\.\d{1,2}\.)\s+(\d{1,2}:\d{2})\]\s*(.+?)\s*$/; + const citationRe = /\[(\d+[a-z]?)\]/g; + + const lookupSource = (num) => { + let src = sources.find(s => String(s.nr) === num || Number(s.nr) === Number(num)); + if (!src && /[a-z]$/.test(num)) { + const baseNum = num.replace(/[a-z]$/, ''); + src = sources.find(s => String(s.nr) === baseNum || Number(s.nr) === Number(baseNum)); + } + return src || null; + }; + + const cards = bulletLines.map(line => { + const m = bulletRe.exec(line); + if (!m) { + const body = this.escape(line.replace(/^-\s*/, '')); + return `