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>
Dieser Commit ist enthalten in:
@@ -1058,8 +1058,14 @@ const UI = {
|
||||
const langs = (s.languages || ['de']).map(l => (l || 'de').toUpperCase()).join('/');
|
||||
const sourceName = this.escape(s.source || 'Unbekannt');
|
||||
const sType = s.source_type || 'web';
|
||||
html += `<div class="source-overview-item" data-source="${sourceName}" data-type="${sType}" tabindex="0" role="button" aria-expanded="false" onclick="App.toggleSourceOverviewDetail(this)" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();App.toggleSourceOverviewDetail(this);}">
|
||||
// Andockpunkt 2: empirischer Track-Record. Nur bei Treffern, dezent.
|
||||
const fimiN = s.fimi_match_count || 0;
|
||||
const fimiBadge = fimiN > 0
|
||||
? `<span class="fimi-source-badge" title="${fimiN} ${fimiN === 1 ? 'Artikel dieser Quelle deckt' : 'Artikel dieser Quelle decken'} sich mit einer bei EUvsDisinfo widerlegten Falschbehauptung">${fimiN} FIMI</span>`
|
||||
: '';
|
||||
html += `<div class="source-overview-item${fimiN > 0 ? ' has-fimi' : ''}" data-source="${sourceName}" data-type="${sType}" tabindex="0" role="button" aria-expanded="false" onclick="App.toggleSourceOverviewDetail(this)" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();App.toggleSourceOverviewDetail(this);}">
|
||||
<span class="source-overview-name">${sourceName}</span>
|
||||
${fimiBadge}
|
||||
<span class="source-overview-lang">${langs}</span>
|
||||
<span class="source-overview-count">${s.article_count}</span>
|
||||
</div>`;
|
||||
@@ -1069,6 +1075,67 @@ const UI = {
|
||||
return html;
|
||||
},
|
||||
|
||||
/**
|
||||
* Andockpunkt 1: dezenter Inline-Hinweis an einem Artikel, der sich mit
|
||||
* einer bei EUvsDisinfo widerlegten Falschbehauptung deckt. Provenienz-
|
||||
* Leitplanke: nennt die Quelle (EUvsDisinfo), verlinkt den Case, wertet
|
||||
* nicht selbst. matches: Array aus dem fimi-matches-Endpunkt.
|
||||
*/
|
||||
renderFimiHint(matches) {
|
||||
if (!matches || matches.length === 0) return '';
|
||||
const n = matches.length;
|
||||
const top = matches[0];
|
||||
const claimText = this.escape(top.claim_text || '');
|
||||
const passage = top.passage ? this.escape(top.passage) : '';
|
||||
let tip = `Bei EUvsDisinfo als widerlegt geführte Behauptung: ${claimText}`;
|
||||
if (passage) tip += ` | Im Artikel: ${passage}`;
|
||||
const label = n === 1
|
||||
? 'Deckt sich mit einer von EUvsDisinfo widerlegten Falschbehauptung'
|
||||
: `Deckt sich mit ${n} von EUvsDisinfo widerlegten Falschbehauptungen`;
|
||||
const link = top.case_url
|
||||
? `<a href="${this.escape(top.case_url)}" target="_blank" rel="noopener" class="fimi-hint-link" onclick="event.stopPropagation()">Beleg ansehen</a>`
|
||||
: '';
|
||||
return `<div class="fimi-hint" title="${tip}">
|
||||
<span class="fimi-hint-icon" aria-hidden="true">⚠</span>
|
||||
<span class="fimi-hint-text">${label}</span>
|
||||
${link}
|
||||
</div>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Andockpunkt 3: Qualitaetsachse fuers Lagebild. Verdichtet die
|
||||
* Einzeltreffer auf Lage-Ebene. Bei 0 Treffern eine ruhige Entwarnung,
|
||||
* sonst eine zurueckhaltende Hinweisleiste mit aufklappbaren Narrativen.
|
||||
*/
|
||||
renderFimiSummaryBar(s) {
|
||||
if (!s || !s.articles_checked) return '';
|
||||
const matched = s.articles_with_match || 0;
|
||||
const checked = s.articles_checked || 0;
|
||||
const distinct = s.distinct_claims || 0;
|
||||
if (matched === 0) {
|
||||
return `<div class="fimi-summary-bar fimi-summary-bar--clear">
|
||||
<span class="fimi-summary-icon" aria-hidden="true">✓</span>
|
||||
<span>Keine bekannten Falschbehauptungen unter ${checked} geprüften Artikeln.</span>
|
||||
</div>`;
|
||||
}
|
||||
const topClaims = (s.top_claims || []).slice(0, 6);
|
||||
const claimList = topClaims.map(c => {
|
||||
const txt = this.escape(c.claim_text || '');
|
||||
const link = c.case_url
|
||||
? `<a href="${this.escape(c.case_url)}" target="_blank" rel="noopener" class="fimi-hint-link">Beleg</a>`
|
||||
: '';
|
||||
return `<li><span class="fimi-claim-count">${c.article_count}×</span> <span class="fimi-claim-text">${txt}</span> ${link}</li>`;
|
||||
}).join('');
|
||||
return `<div class="fimi-summary-bar fimi-summary-bar--alert">
|
||||
<div class="fimi-summary-head">
|
||||
<span class="fimi-summary-icon" aria-hidden="true">⚠</span>
|
||||
<span class="fimi-summary-lead"><strong>${matched}</strong> von ${checked} geprüften Artikeln decken sich mit <strong>${distinct}</strong> bei EUvsDisinfo widerlegten Falschbehauptungen.</span>
|
||||
<button type="button" class="fimi-summary-toggle" onclick="App.toggleFimiDetail(this)">Narrative anzeigen</button>
|
||||
</div>
|
||||
<ul class="fimi-summary-claims" style="display:none;">${claimList}</ul>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
renderSourceOverview(articles) {
|
||||
if (!articles || articles.length === 0) return '';
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren