/** * AnomalyHighlights โ€” prominent UAP / cryptid anomaly panel for the document * page. The clean reading version is the default body, but the investigative * "destaque" of every flagged passage must stay visible regardless of which * view (reading or scan) is active. Identical type+rationale flags are grouped * and each group links to the per-page scan where the anomaly was detected. */ import Link from "next/link"; export interface AnomalyFlag { chunk_id: string; page: number; type: string | null; rationale: string | null; } function clean(v: string | null): string | null { const s = typeof v === "string" ? v.trim() : ""; return s && s.toLowerCase() !== "null" ? s : null; } interface Group { type: string | null; rationale: string | null; // shown only when the group has a single flag count: number; pages: number[]; } // Group by anomaly type so the panel stays a scannable "destaque" overview. // Per-passage rationale is kept only when a type has exactly one flag; the full // per-chunk rationale remains available in the "trechos ยท scan original" view. function groupFlags(flags: AnomalyFlag[]): Group[] { const m = new Map(); for (const f of flags) { const type = clean(f.type); const rationale = clean(f.rationale); const key = type ?? "anomalia"; const g = m.get(key) ?? { type, rationale, count: 0, pages: [] }; g.count += 1; g.rationale = g.count === 1 ? rationale : null; if (!g.pages.includes(f.page)) g.pages.push(f.page); m.set(key, g); } return Array.from(m.values()) .map((g) => ({ ...g, pages: g.pages.sort((a, b) => a - b) })) .sort((a, b) => b.count - a.count || a.pages[0] - b.pages[0]); } function pad(p: number): string { return String(p).padStart(3, "0"); } function PageChips({ docId, pages }: { docId: string; pages: number[] }) { const shown = pages.slice(0, 14); const extra = pages.length - shown.length; return ( {shown.map((p) => ( p{p} ))} {extra > 0 && +{extra}} ); } export function AnomalyHighlights({ docId, ufo, cryptid, }: { docId: string; ufo: AnomalyFlag[]; cryptid: AnomalyFlag[]; }) { if (ufo.length === 0 && cryptid.length === 0) return null; const ufoGroups = groupFlags(ufo); const cryptidGroups = groupFlags(cryptid); return (
{ufo.length > 0 && ( <>

๐Ÿ›ธ Anomalias UAP destacadas ({ufo.length} {ufo.length === 1 ? "trecho" : "trechos"} ยท {ufoGroups.length}{" "} {ufoGroups.length === 1 ? "tipo" : "tipos"})

)} {cryptid.length > 0 && (
0 ? "mt-4 pt-4 border-t border-[rgba(155,93,229,0.25)]" : ""}>

๐Ÿ‘ Anomalias cryptid destacadas ({cryptid.length} {cryptid.length === 1 ? "trecho" : "trechos"})

    {cryptidGroups.map((g, i) => (
  • ๐Ÿ‘ {g.type ?? "anomalia"} {g.count > 1 && ( ร—{g.count} )} {g.rationale && โ€” {g.rationale}}{" "}
  • ))}
)}
); }