// EvidenceReport — a clean, printable one-page summary of an answer and the
// evidence behind it. Rendered off-screen until the user triggers a print; we
// flip a class on so a print-only stylesheet shows just the report.
// No new deps — window.print() does the work.
import { useEffect } from "react";
import { highlightMatch } from "../lib/highlight";
function fmtConfidence(c) {
if (typeof c !== "number") return "—";
return `${Math.round(c * 100)}%`;
}
// One printable evidence row. Mirrors EvidencePanel's metadata line + excerpt,
// but always expanded (a report shows everything) and ink-on-paper styled.
function ReportEvidence({ citation, rank }) {
const meta = [
citation.page != null ? `page ${citation.page}` : null,
`chunk #${citation.chunk_index}`,
citation.score != null ? `relevance ${citation.score}` : null,
]
.filter(Boolean)
.join(" · ");
return (
);
}
export default function EvidenceReport({ result, question, onClose }) {
// Trigger the browser print dialog once mounted, then notify the parent so it
// can drop the report from the tree. afterprint covers both "Print" and
// "Cancel" so we never get stuck in the print-only view.
useEffect(() => {
if (!result) return;
const root = document.documentElement;
root.classList.add("printing-report");
let finished = false;
let fallback;
// Runs exactly once: drop the print-only class and notify the parent so it
// can unmount the report. Must ALWAYS fire (even if printing fails), or the
// parent is left with a stuck report and a later re-export can't reopen.
const done = () => {
if (finished) return;
finished = true;
clearTimeout(fallback);
window.removeEventListener("afterprint", done);
root.classList.remove("printing-report");
onClose?.();
};
window.addEventListener("afterprint", done);
// Let the report paint before opening the dialog.
const t = setTimeout(() => {
try {
window.print();
} catch {
done(); // print blocked/threw — don't leave the report stuck
return;
}
// Safety net for environments where `afterprint` never fires (sandboxed
// iframes / print-blocking browsers): clean up anyway.
fallback = setTimeout(done, 3000);
}, 60);
return () => {
clearTimeout(t);
clearTimeout(fallback);
window.removeEventListener("afterprint", done);
root.classList.remove("printing-report");
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!result) return null;
const citations = result.citations || [];
const meta = result.metadata || {};
const supported = meta.evidence_status === "supported" && !result.abstained;
return (