/** Accessible research UX. All supporting text renders as text, never executable markup. */
import { AlertCircle, BookOpen, CheckCircle2, ExternalLink, FileSearch, HelpCircle } from "lucide-react";
import { useMemo, useState } from "react";
import { normalizeResearchPage, researchEnabled } from "./model.ts";
import type {
CitationView, NotebookView, PassageView, ResearchPage, ResearchWorkspaceProps, SourceView,
} from "./types.ts";
const SUPPORT_LABEL = {
supports: "Supports claim",
partially_supports: "Partially supports",
contradicts: "Contradicts claim",
unverified: "Unverified",
} as const;
function SourceMetadata({source}: {source: SourceView}) {
return
{source.classification} source{source.title}
- Publisher
- {source.publisher || "Not provided"}
Published{source.publishedAt ?
: "Not provided"}
Retrieved
Kind{source.kind.replace("_", " ")}
{source.provenance && Collected by
{source.provenance.actorType} on {source.provenance.surface}
}
{source.uri &&
Open source
}
;
}
function Passage({passage, source}: {passage: PassageView; source: SourceView}) {
return
{passage.text}
{source.title}{passage.locator ? ` · ${passage.locator}` : ""}
;
}
function CitationCard({citation, page}: {citation: CitationView; page: ResearchPage}) {
const [expanded, setExpanded] = useState(false);
const passages = citation.passageIds.map((id) => page.passages.find((item) => item.id === id))
.filter((item): item is PassageView => Boolean(item));
const sourceIds = [...new Set(passages.map((item) => item.sourceId))];
const sources = sourceIds.map((id) => page.sources.find((item) => item.id === id))
.filter((item): item is SourceView => Boolean(item));
const panelId = `hux-citation-${citation.id}`;
const Icon = citation.support === "supports" ? CheckCircle2 :
citation.support === "unverified" ? HelpCircle : AlertCircle;
return
{expanded &&
{citation.note && Research note: {citation.note}
}
{passages.map((item) => {
const source = page.sources.find((candidate) => candidate.id === item.sourceId)!;
return
;
})}
{sources.map((item) =>
)}
}
;
}
function CitationStrip({page, messageId}: {page: ResearchPage; messageId?: string}) {
const citations = messageId ? page.citations.filter((item) => item.messageId === messageId) : page.citations;
return
{citations.map((citation) => )}
{!citations.length && No citations are attached to this answer.
}
;
}
function ItemList({title, values, empty}: {title: string; values: readonly string[]; empty: string}) {
return {title}
{values.length ? {values.map((value, index) =>
- {value}
)}
: {empty}
};
}
function Notebook({notebook, page}: {notebook: NotebookView; page: ResearchPage}) {
const sources = notebook.sourceIds.map((id) => page.sources.find((item) => item.id === id))
.filter((item): item is SourceView => Boolean(item));
return
Collected sources
{sources.length ? {sources.map((source) => -
{source.title}{source.classification} · {source.publisher || source.kind}
)}
: No sources collected.
}
;
}
/** Render nothing unless foundation, friendly modes, and research are negotiated. */
export function ResearchWorkspace(props: ResearchWorkspaceProps) {
const enabled = researchEnabled(props.flags);
const page = useMemo(() => enabled ? normalizeResearchPage(props.page, props.identity, props.scope) : null,
[enabled, props.identity, props.page, props.scope]);
const [selectedId, setSelectedId] = useState(null);
if (!enabled || !page) return null;
const selected = page.notebooks.find((item) => item.id === selectedId) || page.notebooks[0] || null;
return
{props.error && {props.error}
}
{page.rejected > 0 &&
Some evidence was withheld because its scope, provenance, URL, or references were invalid.
}
Research notebooks
Sources, notes, assumptions, and open questions stay together.
{selected ? :
Select a notebook to inspect retained research.
}
{`${page.citations.length} citations and ${page.notebooks.length} notebooks available`}
;
}