Commit graph

260 Commits

Autor SHA1 Nachricht Datum
Claude Code
8b84447ad4 feat(fimi): EUvsDisinfo-Pflicht-Disclaimer + Doku
Rechtslage: EUvsDisinfo (EEAS East StratCom Task Force), Datensatz CC BY-SA 4.0.
Pflichten: Attribution (erfuellt via Case-Links), keine Verfaelschung, Disclaimer
"keine offizielle EU-Position". Disclaimer dezent als graue Fusszeile der
FIMI-Qualitaetsleiste (UI.fimiDisclaimerHtml) und im Tooltip der Einzeltreffer.
CLAUDE.md um FIMI-Abschnitt inkl. Rechtslage ergaenzt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:55:35 +00:00
Claude Code
acac401034 feat(fimi): Frontend Andockpunkte 1-3 + Verifizierer-Robustheit
- Andockpunkt 1: dezenter Inline-Hinweis am Artikel (Quellen-Detailliste)
  mit Provenienz (EUvsDisinfo) + Case-Link, nur bei bestaetigtem Treffer.
- Andockpunkt 2: Track-Record-Badge pro Quelle in der Quellenuebersicht.
- Andockpunkt 3: Qualitaetsleiste ueber dem Lagebild (geprueft/Treffer/
  Narrative), aufklappbare Top-Narrative mit Belegen.
- fimi_matcher: URLs aus dem Artikeltext entfernen + Prompt-Praeambel gegen
  Tool-Nutzung, sonst scheiterte die Haiku-Verifikation an WebFetch-Versuchen
  (error_max_turns).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:43:11 +00:00
Claude Code
d986d611cf feat(export): Ersteller im Export-Dialog manuell eingebbar
Der Export-Dialog hat ein neues optionales Feld "Ersteller". Ist es
gefuellt, wird dieser Name im Bericht als Ersteller verwendet; bleibt es
leer, gilt wie bisher die E-Mail des Lage-Erstellers.

- export_incident: optionaler Query-Parameter creator, hat Vorrang vor
  der E-Mail-Ableitung
- exportReport (api.js) haengt creator an die Export-URL
- submitExport (app.js) liest das neue Feld aus
- Eingabefeld im Export-Modal (dashboard.html)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 19:08:26 +00:00
Claude (claude-dev)
279df0f56b feat(export): neutrale Export-Variante ohne Firmenbranding
Beim Bericht-Export lässt sich im Modal nun zwischen "Mit
AegisSight-Branding" und "Ohne Firmen-Branding" wählen. Im
neutralen Modus entfallen Logo, AegisSight-Zeile auf dem
Deckblatt und Branding-Footer; die Datei-Metadaten werden
neutralisiert. Das Deckblatt mit Titel, Stand und Ersteller
bleibt erhalten. Betrifft PDF und DOCX.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 18:39:21 +00:00
Claude Code
1b8961ca12 fix(sources): Typ-Filter in der Fall-Quellenuebersicht immer anzeigen
Die Filter-Chips wurden nur eingeblendet, wenn ein Fall Telegram- oder
X-Quellen hatte. Bei reinen Web-Faellen (z.B. in der Org jp_demo) fehlte
die Filterleiste damit komplett. Sie wird jetzt immer angezeigt, sobald
Quellen vorhanden sind, und zeigt zugleich, welche Quellentypen der Fall
enthaelt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 16:40:33 +00:00
Claude Code
f69fa1b95e feat(sources): Quellenuebersicht der Lage nach Typ filterbar
Die Quellenuebersicht innerhalb einer Lage zeigt jetzt Filter-Chips
(Alle / Web / Telegram / X) und blendet die Quellen-Boxen nach
Quellentyp ein und aus. Die Chips erscheinen nur, wenn neben Web auch
Telegram- oder X-Quellen vorkommen.

- sources-summary-Endpoint liefert pro Quelle einen source_type,
  abgeleitet aus dem source-Praefix (X: / Telegram: / sonst Web)
- Filter-Chips und data-type in renderSourceOverviewFromSummary
- App.filterSourceOverview blendet die Boxen nach Typ
- Chip-Styles in style.css

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:37:44 +00:00
Claude Code
a0f4572a01 feat(sources): Quellenuebersicht nach Quellentyp filterbar
Der Typ-Filter im Quellen-Modal kennt jetzt auch podcast_feed, damit
alle Quellentypen (RSS, Web, Telegram, X, Podcast) filterbar sind.
Zusaetzlich zeigt jede Quelle ein korrektes Typ-Badge -- vorher zeigten
Telegram, X und Podcast faelschlich "Web".

- podcast_feed im sources-filter-type-Dropdown
- _sourceTypeLabel-Helfer, korrekte Typ-Badges im Gruppen-Header und in
  den Feed-Zeilen, x_account im Info-Tooltip-typeMap

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:31:04 +00:00
Claude Code
9c50439785 feat(x): X (Twitter) als Bezugsquelle pro Lage
X-Accounts werden analog zu Telegram als Quelle (source_type=x_account)
konfiguriert und pro Lage ueber include_x zugeschaltet. Der Scraper
(feeds/x_parser.py, twscrape) liest Account-Timelines, optional ueber
einen HTTP-Proxy mit Fallback auf direkten Abruf ueber die Server-IP.

- DB-Migration include_x, Pydantic-Modelle, incidents-Router
- Orchestrator-X-Pipeline plus Haiku-Account-Vorselektion
- sources-Router /x/validate, x_account-Typ in Stats und Frontend
- Lage-Einstellungen: X-Toggle neben international und Telegram
- twscrape als Abhaengigkeit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 06:52:19 +00:00
d65f0180d9 feat(public-mood): Stimmungs-Kachel aus Foren-Quellen
Eigene Pipeline-Stufe nach factcheck, vor summary, die Foren-Artikel
(media_type='forum') zu einer Themen-Zusammenfassung verarbeitet. Wird als
separate Dashboard-Kachel "Öffentliche Stimmung" angezeigt — getrennt von
Lagebild und Faktencheck, damit anonyme Forenposts nicht mit belegter
Faktenlage verwechselt werden.

- DB-Migration: incidents.public_mood (TEXT) + public_mood_updated_at (TS).
- pipeline_tracker: neuer Pipeline-Step "public_mood" (DE/EN-Labels).
- analyzer.generate_public_mood: Haiku-Call der Foren-Beitraege pro Quelle
  gruppiert und 3-6 thematische Bullets erzeugt, mit expliziter Quellen-
  Herkunft pro Bullet. Bei zu duennem Material gibt's keinen Output.
- orchestrator: neuer Schritt zwischen Factcheck und Summary. Laedt alle
  Foren-Artikel der Lage (via JOIN auf sources), uebergibt sie an den
  Stimmungs-Agent, speichert den Markdown-Text in incidents.public_mood.
- Topic-Filter (analyzer.filter_relevant_articles) markiert Foren-Quellen
  mit [FORUM]-Tag und bekommt im Prompt die Regel, Foren-Artikel weicher
  zu bewerten (Lage-Keyword im Titel reicht). Sie sollen in der Stimmungs-
  Kachel landen, nicht voreilig verworfen werden.
- IncidentResponse-Modell: public_mood/public_mood_updated_at ergaenzt.
- Frontend: neuer Tab "Öffentliche Stimmung" (nur sichtbar wenn Inhalt da),
  eigene Kachel mit Warn-Hinweis "keine Faktenlage". UI.renderPublicMood
  als einfacher Bullet-Renderer.
- dashboard.html Cache-Buster fuer components.js + app.js gebumpt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:20:17 +02:00
Claude Code
168fbc3987 feat(sources): PDF-Upload auch in der Endkunden-App (Kundenquelle)
- POST /api/sources/upload-pdf: tenant-scoped Upload, gleiche Speicher-
  Konvention wie der Verwaltungs-Endpoint (<dirname(DB)>/pdfs/{sha}.pdf).
  Duplikat-Check beruecksichtigt globale Quellen.
- dashboard.html: +PDF-Button in der Quellenverwaltungs-Toolbar +
  eigenes Modal modal-pdf-upload (closeModal-Quotes via &#39;).
- app.js: App.openPdfUpload + _bindPdfUploadFormOnce (Submit nur einmal
  binden).
- api.js: API.upload(path, formData) Helper analog Verwaltung.
2026-05-16 23:57:32 +00:00
Claude Code
892af55269 feat(i18n): Export-Modal + Quellenverwaltung + Chat-Widget + Stats-Bar
- Export-Modal: Titel, Bereiche, Format, alle Checkboxes (Zusammenfassung,
  Recherchebericht / Lagebild, Faktencheck, Quellen), PDF/DOCX, Abbrechen,
  Exportieren.
- Quellenverwaltung-Modal: Title, 8 Filter-Labels (sr-only) + 8 Alle-*
  Default-Optionen, Search-Placeholder + Label, + Quelle-Button, Add-
  Form (URL/Erkennen/Name/Kategorie/Typ/RSS-URL/Domain/Notizen +
  Placeholder), Speichern/Abbrechen, Loading-State.
- Stats-Bar (app.js): RSS-Feeds/Web-Quellen/Ausgeschlossen-Labels.
- components.js: source-excluded-badge.
- Chat-Widget: Title, alle 5 Buttons mit title+aria, Input-Placeholder.
- Chat-Begruessung in chat.js auf T() umgestellt.
- 50+ neue i18n-Keys. Cache-Buster components.js + chat.js + app.js
  auf v=20260514e gebumpt.
2026-05-13 22:22:07 +00:00
Claude Code
ea630cd31b feat(i18n): grosser Sweep -- Toasts, Confirms, Notification-Center, Map, Empty-States, Lizenz-Hinweise
29 Stellen im Frontend lokalisiert (Toasts: Lage aktualisiert/geloescht/
archiviert/wiederhergestellt, Recherche abgebrochen, Daten aktualisiert,
Quelle hinzugefuegt/aktualisiert, Bericht heruntergeladen, kein RSS;
Confirms: Lage loeschen, Recherche abbrechen; Button-States: Wird
gestartet/abgebrochen/erstellt/gesendet, Suche Feeds, Quelle speichern;
Lizenz: abgelaufen/keine/Org-deaktiviert -- Nur Lesezugriff;
Notification-Center: Titel, Alle gelesen, Keine Benachrichtigungen;
Empty-States: Kein Vorfall ausgewaehlt; Map: Orte einlesen + Tooltip,
Keine Orte erkannt; Modal-Hint: Nur deutschsprachige Quellen). 30+
neue i18n-Keys. Cache-Buster app.js auf v=20260514c.
2026-05-13 22:16:42 +00:00
Claude Code
4fc3212e2c fix(i18n): Notify-Summary-Toggle wird beim Lage-Edit ueberschrieben
app.js:1037-1043 setzte den Text der notify-summary-Checkbox dynamisch
auf Neues Lagebild / Neuer Recherchebericht und damit das data-i18n-
Attribut zurueck. Jetzt ueber T() mit Forschungs-/Lagebild-Varianten.
Neuer Key modal.notify.summary_research.
2026-05-13 22:09:06 +00:00
Claude Code
3a68097b4f feat(i18n): Aktions-Buttons dynamisch + komplettes Neue-Lage/Bearbeiten-Modal
- _updateRefreshButton + _updateArchiveButton (app.js) nutzen T() statt
  Hardcode -- Aktualisieren/Laeuft/Wiederherstellen/Archivieren/Lesemodus.
- Modal-Title-Setter (Lage bearbeiten / Neue Lage anlegen) lokalisiert
  an drei Stellen (init / openEdit / closeModal).
- updateVisibilityHint und toggleTypeDefaults: dynamischer Text via T().
- HTML: ~31 data-i18n + data-i18n-attr im modal-new (Art der Lage,
  Optionen, Type-Hint, Quellen-Toggles, Sichtbarkeit, Aktualisierung,
  Intervall-Einheiten, Aufbewahrung, E-Mail-Toggles, Abbrechen).
- Cache-Buster app.js auf v=20260514a.
2026-05-13 22:05:31 +00:00
Claude Code
90f0731a86 feat(i18n): Aktionsleiste + Sidebar (Quellen, Feedback, Archiv, Stats, Empty-States)
- 5 Action-Buttons im Header (Aktualisieren/Bearbeiten/Bericht
  exportieren/Archivieren/Loeschen) via data-i18n.
- Sidebar Archiv-Section, Quellen-Button, Feedback-Button, title-
  Attribute via data-i18n + data-i18n-attr.
- Sidebar-Stats 0 Quellen / 0 Artikel: app.js.updateSidebarStats
  baut die Suffixe ueber T() zusammen.
- Empty-States Kein Live-Monitoring / Keine Deep-Research (inkl.
  eigene-Filter-Varianten) lokalisiert.
- Cache-Buster app.js auf v=20260513g.
2026-05-13 22:00:00 +00:00
Claude Code
917c260298 fix(i18n): Tab-Labels werden dynamisch ueberschrieben -- T() statt hardcode
LayoutManager.applyTypeLabels(layout.js:58-65) und App-Render
(app.js:1063,1081) ueberschreiben die Tab-Texte je nach Lage-Typ.
Beides nutzt jetzt T() mit DE-Fallback. Neue Keys tab.summary_short
und tab.summary_report. Cache-Buster layout.js + app.js gebumpt.
2026-05-13 21:51:49 +00:00
Claude Code
9e3c9559d9 feat(i18n): Progress-Popup + Pipeline-Stati lokalisieren
- components._getStepLabel und progress-popup-title nutzen T()
  fuer Erste Recherche laeuft / Aktualisierung laeuft / In Warteschlange
  / Wird abgebrochen.
- pipeline._formatHeader / _relativeTime / _formatCount lokalisiert:
  Status-Texte (erledigt/laeuft/Fehler), Zeitangaben (gerade eben,
  vor X Min/Std/Tagen), Aktualisierung-laeuft-Header.
- dashboard.html: data-i18n auf pipeline-empty, progress-popup-title,
  progress-check-label (4 Stueck).
- Cache-Buster fuer components.js + pipeline.js auf v=20260513d.
2026-05-13 21:45:18 +00:00
Claude Code
b214249a34 fix(i18n): Beschreibung-generieren-Button + Fehler-Toasts uebersetzbar
- Button-Span enhance-btn-text bekommt data-i18n.
- app.js: Loading-State Wird generiert... / Generating... per T().
- Vier Fehler-Toasts (Default, 503, 429, 504) per T() lokalisiert.
- Neue Keys enhance.* in de.json + en.json.
- Cache-Buster app.js auf v=20260513c gebumpt.
2026-05-13 21:39:36 +00:00
Claude Code
3f0e680446 feat(frontend): Light-i18n + Org-Sprache durch /auth/me
Backend:
- UserMeResponse um output_language (de | en) erweitert.
- /auth/me liefert die Org-Sprache aus organization_settings.

Frontend:
- Neu: static/js/i18n.js mit T(key)-Helper, I18N.load(lang) und
  applyDom() ueber data-i18n + data-i18n-attr.
- Neu: static/i18n/de.json + en.json (sichtbare Bereiche: Sidebar,
  Header, Modal-Titel, Faktencheck-Status, Refresh-Hinweise).
- dashboard.html: i18n.js Script-Tag vor api.js, data-i18n auf den
  prominenten Strings (Abmelden, + Neuer Fall, Alle/Eigene, Sidebar-
  Sektionen, Bericht exportieren, Faktencheck-Tab, Lage anlegen).
  Tutorial.init() entfernt aus DOMContentLoaded.
- components.js: factCheckLabels/Tooltips/ChipLabels als Getter ueber
  T() mit DE-Fallbacks.
- app.js: vor Setup wird I18N.load(user.output_language) aufgerufen und
  applyDom() ausgefuehrt. Tutorial.init() laeuft nur bei lang === de.

Phase 6 von 8 (eng_demo / Org-Sprache).
2026-05-13 21:14:56 +00:00
Claude (info@aegis-sight.de)
5ec4480598 fix(incidents): refresh_mode beim Edit nicht durch toggleTypeDefaults überschreiben
Beim Öffnen des Bearbeiten-Dialogs einer Recherche-Lage (type=research) hat
toggleTypeDefaults() den Aktualisierungs-Select hartcodiert auf manual gesetzt
und damit den tatsächlichen DB-Wert im UI verdeckt. User glaubte, manuell sei
gewählt, in der DB stand aber auto und die Lage lief weiter im Auto-Refresh.

Fix: toggleTypeDefaults erhält einen optionalen Parameter preserveMode.
handleEdit ruft mit preserveMode=true auf, damit der DB-Wert respektiert
wird; bei Typ-Wechsel und Neuanlage bleibt der Default-Reset auf manual
für research erhalten.

Cache-Buster app.js: 20260501h -> 20260512a.
2026-05-12 21:02:04 +00:00
Claude Code
b90e47ff3f refactor(klassifikation): Klassifikation aus Monitor entfernt — Pflege jetzt in der Verwaltung
Endpoints unter /api/sources/classification/* weg, Service-Module (source_classifier, external_reputation) gelöscht. Quellen-Modal verliert Tab Klassifikations-Review, Klassifikations-Section in der Edit-Form, alle Bulk-Buttons (Sync, Klassifikation starten, Bulk-Approve). API-Methoden in api.js entfernt, alignment-Helper raus, saveSource entschlackt.

Read-Only bleibt: Filter-Dropdowns über der Quellenliste (Politik, Medientyp, Reliability, Externe Reputation, Alignment) und Inline-Badges (_renderClassificationBadges + Label-Maps in components.js). Kunde sieht nur freigegebene Werte.

GET /api/sources liefert weiter Klassifikations-Felder + alignments für die Anzeige; SourceCreate/SourceUpdate akzeptieren keine Klassifikations-Felder mehr.

Bulk-Klassifikations-Skripte entfernt — Pflege läuft über Verwaltungs-UI.
2026-05-09 22:01:20 +00:00
Claude Code
5fc2467559 feat(sources): externer Reputations-Layer (IFCN + EUvsDisinfo)
Externe Datenquellen (kostenlos, Open Data) ergaenzen die LLM-geschaetzte
Reliability-Achse mit objektiven Signalen:

- IFCN-Signatories (raw.githubusercontent.com/IFCN/verified-signatories):
  Plain-Text-Liste anerkannter Faktencheck-Organisationen.
- EUvsDisinfo (Zenodo CSV): Pro-Kreml-Desinformations-Datenbank.

Schema-Erweiterung:
- ifcn_signatory, eu_disinfo_listed, eu_disinfo_case_count,
  eu_disinfo_last_seen, external_data_synced_at.

Service src/services/external_reputation.py:
- sync_ifcn_signatories(), sync_eu_disinfo(), apply_reputation_overrides(),
  sync_all() mit Domain-Normalisierung (lowercase, ohne www., ohne Schema).

Reliability-Override-Regeln (laufen nach Approve und manuellem Sync):
- ifcn_signatory=1 -> reliability=sehr_hoch
- eu_disinfo_case_count >= 5 -> reliability=sehr_niedrig
- eu_disinfo_case_count >= 1 -> Reliability eine Stufe runter (max niedrig)

API: POST /api/sources/external-reputation/sync (Admin, BackgroundTask).
Filter: ?ifcn_signatory=true, ?eu_disinfo_listed=true.

UI:
- Filter-Dropdown "Externe Reputation" im Quellen-Modal.
- Badges: gruenes "IFCN" und rotes "EU-Desinfo (n)".
- Tooltip macht Reliability-Quelle transparent: "(IFCN-Faktenchecker)",
  "(EU-Desinfo, n Faelle)" oder "(LLM-Schaetzung)".
- "Externe Daten syncen"-Button im Review-Toolbar (Admin-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:40:30 +00:00
Claude Code
48a60d7579 feat(sources): Review-Queue-UI fuer LLM-Klassifikations-Vorschlaege (Admin)
- Tab-Schalter im Quellen-Modal: "Quellenliste" vs. "Klassifikations-Review"
  (Review-Tab nur fuer org_admin sichtbar, mit Pending-Counter-Badge).
- Review-Karten zeigen Diff aktueller Wert -> LLM-Vorschlag pro Achse,
  Konfidenz-Indikator (gruen/gelb/rot), LLM-Begruendung, Buttons fuer
  Uebernehmen / Verwerfen / Neu klassifizieren.
- Toolbar: Konfidenz-Filter, "Klassifikation starten" (Bulk im Hintergrund),
  "Alle >= 0.85 genehmigen" (Bulk-Approve).
- API-Wrapper in api.js fuer alle 6 neuen Endpoints + erweiterte listSources-Filter.
- Backend-Endpoint POST /api/sources/classification/bulk-approve (Admin-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:00:47 +00:00
Claude Code
715af17ac3 feat(sources): UI fuer Quellen-Klassifikation (Filter, Badges, Edit-Form)
- Quellen-Modal: 4 neue Filter (Politik, Medientyp, Reliability, Alignment).
- Edit-Form: Selects fuer political_orientation/media_type/reliability,
  Multi-Select-Chips fuer alignments, Toggle state_affiliated, Country-Code-Input.
- renderSourceGroup: Politik-Badge mit DACH-Farbskala (rot=L, blau=R),
  Reliability-Punkt (gruen→rot), Alignment-Tags, state-affiliated-Indikator.
  Tooltip um alle 4 Achsen erweitert.
- CSS-Block fuer alle neuen Badge-/Chip-Styles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:37:09 +00:00
Claude Code
b1a0e97a34 Pipeline: bei Lagen-Wechsel auf bereits-queued Lage automatisch beginQueue
Wenn der User in der Sidebar auf eine Lage klickt, die schon in Queue
wartet, ruft bindToIncident() die API auf und kriegt den letzten
gespeicherten Pipeline-Stand (alles done = gruen). Das ist falsch fuer
queued-Status.

Fix: nach API-Load pruefen, ob die Lage in App._refreshingIncidents ist
UND in UI._progressState mit step=queued -> beginQueue() selbst ausloesen.
Damit zeigt die Pipeline grau, sobald man auf die queued-Lage wechselt.
2026-05-03 14:27:20 +00:00
Claude Code
77797f6027 Refresh-Modal: Titel je nach Status (queued/cancelling/laeuft)
Bisher hing der Titel nur an state.isFirst -> stand auch "Aktualisierung
laeuft" wenn die Lage tatsaechlich noch in der Queue wartete.

Jetzt:
- queued    -> "In Warteschlange" (mit Position #N falls vorhanden)
- cancelling -> "Wird abgebrochen…"
- isFirst   -> "Erste Recherche laeuft"
- sonst     -> "Aktualisierung laeuft"
2026-05-03 14:18:17 +00:00
Claude Code
dc51ecafe8 Pipeline-Snapshot: Mini-Pipeline auch zuruecksetzen
beginQueue() und _restoreSnapshot() haben bisher nur _render() aufgerufen,
aber NICHT _renderMini(). Daher blieben die kleinen Pipeline-Icons im
"Aktualisierung laeuft"-Modal gruen, obwohl die Lage in Queue war.
Fix: an beiden Stellen auch _renderMini() aufrufen.
2026-05-03 14:15:27 +00:00
Claude Code
31fa17465a Pipeline-Icons: Snapshot/Restore bei Queue + Cancel
Vorher:
- Lage refreshen -> Lage geht in Queue, aber Pipeline-Icons bleiben gruen
  mit Haekchen vom letzten Refresh (suggeriert faelschlich "alles fertig")
- Cancel/Error -> Pipeline bleibt im Mix-Zustand (teils active, teils pending)

Nachher:
- pipeline.beginQueue(id): macht Snapshot des aktuellen _stateByKey und
  setzt alle Steps auf pending. Ausgeloest aus app.js handleRefresh()
  und _restoreRefreshingState() (auch nach F5).
- _onRefreshDoneSuccess: Snapshot verwerfen + API-Reload (wie bisher).
- _onRefreshDoneCancel: Snapshot zurueckspielen -> vorheriger gruener
  Stand sichtbar.
- _onRefreshDoneError: gleiches Verhalten wie Cancel.
- bindToIncident: Snapshot mitloeschen (lagen-spezifisch).
- Bei zweitem Refresh ohne Cancel dazwischen wird Snapshot bewusst
  ueberschrieben.
2026-05-03 14:10:56 +00:00
Claude Code
2a654cc882 AI-Disclaimer: Modell-Name (Claude/Anthropic) aus Text entfernt 2026-05-03 13:42:35 +00:00
Claude Code
6293cef91e Banner-Text + AI-Disclaimer-Modal + Translator-Robustheit
#28 Banner-Text bei Token-Budget aufgebraucht:
- middleware/license_check.py + static/js/app.js: Statt "Bitte Verwaltung
  kontaktieren" jetzt konkreter Upgrade-Pfad mit info@aegis-sight.de.

#29 AI-Hallucination-Disclaimer:
- Neue static/js/ai-disclaimer.js (analog zu update-system.js):
  IIFE-Modul, localStorage-versioniert (aegis_ai_disclaimer_seen=v1),
  inline-CSS mit Theme-Variablen, Modal mit Lucide-Info-Icon.
- Wird beim ersten Login einmalig gezeigt; ueber Header-User-Dropdown
  Eintrag "Ueber KI-Inhalte" jederzeit erneut oeffenbar.
- dashboard.html: Script-Tag + Dropdown-Button mit Lucide-SVG.
- style.css: kleiner Stil-Block fuer .header-dropdown-action.

Translator-Robustheit (Bonus):
- agents/translator.py: Parser akzeptiert jetzt auch von Claude wrapped
  Antworten ({{translations: [...]}}, {{items: [...]}}, einzelnes
  Object). Behebt Wrapper-Bug der gestern beim Backfill 75% der Calls
  fehlschlagen liess.
- Prompt deutlicher: "flaches JSON-Array, kein Wrapper".
2026-05-03 13:29:19 +00:00
Claude Code
ee83f38edf Token-Budget Hard-Stop + Banner bei aufgebrauchtem Budget
- check_license() liefert jetzt unlimited_budget, credits_total, credits_used,
  read_only_reason. Bei nicht-unlimited UND credits_used >= credits_total wird
  status=budget_exceeded, read_only=True gesetzt.
- require_writable_license blockiert mit 403 + X-License-Status-Header je nach Reason.
- /api/auth/me liefert read_only_reason und unlimited_budget; credits_percent_used
  wird nicht mehr auf 100 gekappt (echte Prozente).
- Frontend: Banner-Text dynamisch je nach reason (budget_exceeded/expired/...).
  Refresh-Button bei read_only deaktiviert + Tooltip. Globaler 403-Handler in
  api.js: bei X-License-Status -> Banner + Toast aktualisieren.
2026-05-02 20:16:25 +00:00
26fac0e824 Analysepipeline: Reset auf "pending" beim Refresh-Start
Beim ersten Schritt (sources_review) eines neuen Refreshs werden alle
nachfolgenden Schritte sichtbar auf "pending" (grau) zurückgesetzt.
Vorher hingen sie weiterhin als "done" vom letzten Refresh in grün
herum, während die Pipeline schon einen neuen Durchlauf zeigte.

- Bedingung in pipeline.js entschärft: nicht mehr nur bei
  pass_number > 1 (Multi-Pass), sondern bei jedem ersten Schritt-Active
- Bei Reset wird das ganze Stage neu gezeichnet (nicht nur der einzelne
  Block), damit die zurückgesetzten Schritte tatsächlich grau erscheinen
- Greift sowohl bei normalem Refresh als auch bei Multi-Pass-Wechsel
  einer Research-Lage

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:06:06 +02:00
ad5b723d79 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>
2026-05-01 18:04:52 +02:00
51615cae62 Quellenübersicht: Detail-Liste mit Nummer, Datum und Link
Aufklapp-Liste pro Quelle zeigt jetzt:
1. fortlaufende Nummer (gold, monospace)
2. Datum + Uhrzeit (klein, dezent grau, monospace)
3. Headline als Link zum Originalartikel

Drei-Spalten-Grid (Nummer | Datum | Headline). Auf schmalem Viewport
(<600px) klappt das Datum unter die Nummer. Bei research-Lagen wird
published_at bevorzugt, sonst collected_at.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:01:06 +02:00
a2610d0094 Quellenübersicht: Klick auf Quelle klappt Artikel-Liste auf
Quellen-Boxen waren bisher reine Anzeige. Jetzt sind sie klickbar:
beim Klick erscheint direkt unter der Box (über die volle Grid-Breite)
eine Liste der Artikel-Headlines dieser Quelle, jede mit Link zum
Originalartikel. Mutual-exclusive — Klick auf eine andere Quelle
schließt die vorherige automatisch.

- components.js: Item bekommt data-source, onclick + Tastatur-Support
  (Enter/Space), aria-expanded.
- app.js: toggleSourceOverviewDetail filtert _currentArticles nach
  Quelle, sortiert chronologisch absteigend, fügt das Detail-Element
  via insertAdjacentElement direkt nach dem geklickten Item ein.
- CSS: aktiver Item-Status (Glow + Tint), Detail-Block mit
  grid-column 1/-1 (volle Breite) + max-height 320px scrollbar bei
  vielen Artikeln + dezente Slide-In-Animation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:57:48 +02:00
462127dc52 Ereignis-Timeline: Heatmap-Klick-Bug beheben
Inline-onclick mit JSON.stringify(label) + UI.escape erzeugte bei
Bucket-Labels mit Anführungszeichen oder Sonderzeichen einen kaputten
HTML-Attribut-String. Klicks lösten daher gar keinen Handler aus.

Statt JS-String im onclick werden Bucket-Daten jetzt als
data-start/data-end/data-label-Attribute am Cell-Element gehalten.
Onclick ruft App.handleStripClick(this), das die Werte sauber aus
dataset liest und an openTimelineWindow weiterreicht.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:04:02 +02:00
34aeb04a88 Ereignis-Timeline: Klick auf Heatmap-Balken filtert den Stream
Vorher scrollte ein Klick auf einen Balken nur zur passenden Zeit-
Gruppe — bei langem Stream kaum erkennbar. Jetzt filtert der Klick
den Stream auf das Zeitfenster des Balkens und zeigt nur diese
Einträge.

- Aktiver Balken: vergrößert (scaleY 1.6) + goldener Hintergrund +
  starker Glow + kleiner ▼-Pfeil darunter; alle anderen Balken auf
  40% Opacity gedimmt.
- Banner zwischen Strip und Stream zeigt "Gefiltert auf [Label] ·
  X Einträge" mit "Filter aufheben"-Button.
- Zweiter Klick auf denselben Balken oder Banner-Button hebt den
  Filter auf.
- Filter/Range-Buttons setzen den Strip-Window-Filter zurück (sonst
  inkonsistente Doppel-Filterung).
- Lagen-Wechsel räumt _activeStripWindow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:59:32 +02:00
b14fe31f42 Ereignis-Timeline: Newsfeed mit Lagebericht-Sektionen + Heatmap-Strip
Komplett neu gedacht: nicht mehr horizontale Karten-Kette, sondern
vertikaler Newsfeed mit den vorhandenen vt-Klassen, plus dezenter
Heatmap-Strip oben für die Quantitäts-Übersicht.

- Heatmap-Strip oben (14 px hoch): ein Quadrat pro Tag/Stunde/Woche/
  Monat je nach Spannweite, Farbintensität = Aktivität, goldener
  Boden-Strich bei Lagebericht.
- Klick auf Heatmap-Quadrat: Stream scrollt zur passenden Zeit-Gruppe,
  diese flasht kurz auf.
- Newsfeed darunter: vt-time-group mit Datums-Trennzeilen
  (Heute/Gestern/...), Lagebericht-Einträge sind durch ihre vt-snapshot
  Klasse prominent gegenüber Meldungs-Einträgen.
- Klick auf Lagebericht: Volltext klappt inline auf (vorhandener
  lazyLoadSnapshotDetail-Mechanismus, kein separates Detail-Panel mehr).
- Klick auf Meldung: Detail klappt inline auf.

Karten-Kette, Verbindungs-Stränge, "Aktuell"-Marker, Snapshot-Detail-
Panel, Window-Detail-Panel und alle zugehörigen CSS-Klassen
(ht-card/ht-link/ht-now/ht-chain/ht-detail) komplett entfernt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:51:41 +02:00
ffb8dddc4f Ereignis-Timeline: Snapshot-zentriertes Konzept
Komplette Neufassung der horizontalen Timeline. Lageberichte sind die
natürlichen Anker einer OSINT-Lage; Artikel werden um sie herum
gruppiert.

Aufbau:
- Quanti-Strip oben: schmale Heatmap-Reihe (ein Quadrat pro Stunde/Tag/
  Woche/Monat je nach Spannweite), Farbintensität = Aktivität. Quadrate
  mit Lagebericht haben goldene Unterkante. Klick auf Quadrat öffnet
  Detail-Panel mit allen Meldungen des Zeitfensters.
- Lagebericht-Kette darunter: jede Karte zeigt Datum, Vorschau-Text aus
  dem Snapshot, Anzahl Meldungen + Fakten. Karten sind durch Stränge
  verbunden, die "X Meldungen"-Pille tragen — Klick auf Strang öffnet
  Liste der Meldungen zwischen den beiden Lageberichten.
- "Aktuell"-Marker am rechten Ende mit pulsierendem Pin.

Filter:
- Alle: Strip + Kette
- Meldungen: Strip + vertikaler Stream
- Lageberichte: nur Karten ohne Strip/Stränge

Edge-Case: Lagen ohne Lagebericht zeigen Strip + Stream als Fallback.

Mobile (<900px): Kette stapelt vertikal, Strip bleibt horizontal.

Alte Bar-Achse, Punkte, Bucket-Merge, Day-Markers etc. komplett
entfernt — die alte Achse war für sporadische OSINT-Aktivität das
falsche Pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:38:09 +02:00
AegisSight Promote-UI
0edbf7e3b8 Revert "Ereignis-Timeline: Säulen, Lagebericht-Linien, Themen-Labels"
This reverts commit 370bb94b26.
2026-05-01 15:22:13 +02:00
AegisSight Promote-UI
de01ab71fc Revert "Ereignis-Timeline: Überlappungen oben auflösen"
This reverts commit 58eb1298ca.
2026-05-01 15:22:06 +02:00
AegisSight Promote-UI
86a49e082c Revert "Ereignis-Timeline: Lagebericht-Stempel zusammenfassen, Bar-Cap entfernen"
This reverts commit cae9c5467a.
2026-05-01 15:21:53 +02:00
cae9c5467a Ereignis-Timeline: Lagebericht-Stempel zusammenfassen, Bar-Cap entfernen
Mehrere Snapshots in derselben Achsen-Position erzeugten verschmierte,
übereinanderliegende Pin-Symbole. Zusätzlich war der goldene Streifen
auf der Bar (Bar-Cap) redundant zur vertikalen Snapshot-Linie.

- Snapshots werden pro Achsen-Position (auf 0,5%-Genauigkeit) gruppiert.
  Eine einzige Linie + ein einziger Pin pro Gruppe; bei mehreren
  Lageberichten zeigt der Pin die Anzahl als Zahl statt das Stempel-
  Symbol.
- Bar-Cap (separates Element über der Bar) entfernt. Stattdessen
  bekommt die Bar-Füllung bei has-snapshot eine dezente goldene
  Top-Linie via ::before — keine Doppel-Markierung mehr.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:13:17 +02:00
58eb1298ca Ereignis-Timeline: Überlappungen oben auflösen
Im Top-Bereich der Achse kollidierten Tagesmarker, Themen-Labels und
Lagebericht-Stempel auf der gleichen Höhe. Jetzt klare Schichten:

- Tagesmarker (Heute/Gestern/Datum): top 0
- Themen-Labels: eigene Schiene direkt darunter (top 22 / 42 hourly),
  nicht mehr Kind der Bar — verhindert Wandern bei verschieden hohen
  Bars
- Bars: nach unten verschoben (top 44 / 64 hourly)
- Lagebericht-Linien: gehen jetzt nur durch den Bar-Bereich,
  Pin-Symbol (Cap) hängt UNTEN an der Achsenlinie statt oben in den
  Tagesmarkern
- Heute-Linie: bei stündlicher Granularität ausgeblendet (Tagesmarker
  zeigt eh "Heute, ..."), bei Tag/Woche/Monat weiterhin aktiv

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:10:16 +02:00
370bb94b26 Ereignis-Timeline: Säulen, Lagebericht-Linien, Themen-Labels
Punkte ersetzt durch schmale Säulen (Bar-Chart), Höhe = Anzahl Artikel
im Bucket relativ zum Maximum. Aktivität ist sofort als Verlauf lesbar.

- Granularität: hour < 48h, day < 30T, week < 180T, sonst month.
  Bucket-Merge (verfälscht das Datum) entfernt, stattdessen sauberer
  Granularitätswechsel.
- Lagebericht-Linien quer durch die Achse als dezente goldene Vertikalen
  mit kleinem Stempel-Symbol oben. Klick öffnet das Bucket-Detail mit
  dem zugehörigen Snapshot.
- Heute-Linie mit Label, wenn der heutige Zeitpunkt im sichtbaren
  Bereich liegt.
- Themen-Label über den Top-3 aktivsten Buckets: clientseitig per
  Wort-Häufigkeit aus Headlines, mit deutscher Stopwortliste. Zeigt
  nur, wenn ein Wort mindestens zweimal vorkommt.
- Hover über eine Säule: Mini-Karte mit den 3 relevantesten Headlines
  des Buckets (sortiert nach relevance_score), plus "+N weitere" und
  Lagebericht-Hinweis bei gemischten Buckets.
- Snapshot-Bars bekommen oben einen goldenen Cap als Marker.
- Reduced-motion respektiert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:04:43 +02:00
392028a9aa Analysepipeline: kompakter Reihenwechsel-Pfeil statt langem Bogen
Der U-Turn-Bogen, der quer ueber die ganze Box-Breite lief, wirkte bei
nur drei Bloecken pro Reihe optisch ueberladen. Jetzt sitzt unter dem
letzten Block der oberen Reihe ein schlichter, kurzer Pfeil nach unten,
der direkt zum ersten Block der naechsten Reihe zeigt.

- pipeline.js: Neue _renderUturn-Variante, die Spacer (Block-Breite)
  vor oder hinter den Pfeil setzt, sodass er passgenau unter dem letzten
  Block sitzt (rechts nach ltr-Reihe, links nach rtl-Reihe).
- style.css: Pfeil-Container nutzt Flex mit Block-Breite-Spacern statt
  100%-breitem SVG. Kurzer ↓ als gerader Pfad mit Pfeilkopf,
  is-flowing-Animation bleibt erhalten.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:35:13 +02:00
7b5adccf2b Analysepipeline: echte Umlaute und ASCII-Bindestriche
Em-dashes und Umlaut-Umschreibungen aus den Pipeline-Aenderungen
entfernt: Tooltip-Texte, HTML-Empty-State, JS-Kommentare,
Count-Status-Platzhalter, Orchestrator-Kommentare und CSS-Kommentare.

Anstelle von typografischen Gedankenstrichen werden jetzt Kommas oder
Punkte gesetzt, "uebersprungen" -> "uebersprungen" mit echtem Umlaut,
"laeuft" usw. analog. UI-Text "&mdash; Refresh starten" wird zu zwei
Saetzen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:16:28 +02:00
059a9a2dc7 Analysepipeline: Snake-Layout (3x3) statt linearer Reihe
Pipeline laeuft jetzt zickzack: Reihe 1 von links nach rechts, U-Turn
nach unten, Reihe 2 von rechts nach links, U-Turn nach unten,
Reihe 3 wieder von links nach rechts. Karte waechst auf benoetigte
Hoehe statt horizontalem Scrollen.

- pipeline.js: Bloecke werden in Dreier-Gruppen aufgeteilt, Direction
  ltr/rtl wechselt pro Reihe. Zwischen Reihen rendert ein SVG-U-Turn-Pfeil
  (Bogen mit Pfeilkopf) die Verbindung. Daten-Fluss-Animation (is-flowing)
  funktioniert sowohl auf Inner-Pfeilen als auch auf U-Turns.
- CSS: .pipeline-row mit flex-direction abhaengig von data-direction.
  rtl-Reihen kippen Pfeilkopf und Animation in entgegengesetzte Richtung.
  U-Turn-Pfad als SVG mit stroke-dasharray-Animation bei aktivem Fluss.
- Mobile (<900px): Snake aufgeloest, alle Reihen werden vertikal
  gestapelt, U-Turns ausgeblendet — bestehende Vertikal-Stilistik bleibt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:07:58 +02:00
3a346ba2ec Analysepipeline: Visualisierung der Refresh-Schritte
Neuer Tab "Analysepipeline" zwischen Faktencheck und Quellenuebersicht.
Zeigt 9 Verarbeitungsschritte als n8n-artige Blockkette: Quellen sichten,
Nachrichten sammeln, Doppeltes filtern, Relevanz bewerten, Orte erkennen,
Lagebild verfassen, Fakten pruefen, Qualitaetscheck, Benachrichtigen.

- Backend: refresh_pipeline_steps-Tabelle persistiert pro Refresh+Pass die
  Status- und Zahlen-Werte. pipeline_tracker.py kapselt Start/Done/Skip/Error
  inkl. WebSocket-Broadcast (Event-Typ pipeline_step). 9 Hooks im Orchestrator
  speisen die Anzeige.
- API: GET /api/incidents/{id}/pipeline liefert Definition + letzten Stand
  (Zahlen aus letztem Refresh, Multi-Pass-Konsolidierung).
- Frontend: pipeline.js rendert Vollbild-Blockkette mit pulsierendem Glow am
  aktiven Block, animierten Pfeilen bei Datenfluss, Haekchen am fertigen Block.
  Hover-Tooltip mit Erklaerung in Nutzersprache, Klick oeffnet Detail-Popup.
  Bei Research-Lagen leuchtet ein Schleifen-Pfeil pro Mehrfach-Durchlauf auf.
  Mini-Variante (nur Icons) im Refresh-Progress-Popup.
- CSS: Light/Dark-Theme-fest, dezenter Circuit-Hintergrund (5% Opacity),
  Mobile-vertikale Stapelung unter 900px, prefers-reduced-motion respektiert.
- Uebersprungene Schritte (z.B. Geoparsing ohne neue Artikel) werden
  ausgeblendet, brandneue Lagen ohne Refresh zeigen Hinweis.

Tooltips bewusst in normaler Sprache ohne Internas (keine Modellnamen,
keine Toolnamen, keine Phasen-Labels).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 13:53:44 +02:00
Claude Code
e3fe7fac85 fix(blur): Refresh-Blur stabilisieren und Header mit-blurren
Problem: Beim Anlegen einer neuen Lage verschwand der Blur-Effekt auf dem
Hintergrund-Inhalt sobald das Browserfenster in der Groesse veraendert wurde.
Zudem blieb der Lagen-Titel im Header offen sichtbar, waehrend der Inhalt
darunter geblurrt war — der wechselnde Titel war also klar lesbar.

Ursache:
- Blur lag auf .tab-panels und parallel auf .tab-panel — zwei verschachtelte
  Composite-Layer, die bei jedem Reflow neu berechnet werden.
- transition: filter 0.4s ease auf .tab-panel — bei Resize lief die Transition
  oft rueckwaerts oder pausierte, was den Blur visuell verschwinden liess.
- .incident-header-strip lag ausserhalb von .tab-panels und war dadurch nie
  geblurrt (Titel/Aktionen/Beschreibung blieben offen sichtbar).

Aenderungen:
- Blur-Anker hochgezogen auf #incident-view (Klasse refresh-blurred), so dass
  Header und Tab-Panels gemeinsam unscharf werden.
- Nur noch eine Filter-Ebene (filter: blur(8px)).
- Transition entfernt — Blur soll schlagartig kommen und gehen, kein
  lesbarer Zwischenzustand beim Reflow.
- will-change: filter; transform: translateZ(0); — erzwingt einen persistenten
  GPU-Composite-Layer, der bei Window-Resize stabil bleibt.

Headless-Tests bestaetigen: alte Variante 89.8% Pixel-Stabilitaet ueber 6
Resize-Zyklen mit Content-Mutation, neue Variante 97.0% (Rest = Content-Diff).
2026-04-30 22:40:51 +00:00