"use client"; import { createContext, memo, useContext, type AnchorHTMLAttributes, type ReactNode } from "react"; import { Markdown } from "yt-dlp-transcript-common/components/Markdown"; import type { CitationView, ReportPageView } from "yt-dlp-transcript-common/lib/report/views"; // The article's text, as blocks the notes anchor to (`data-block`: title, // subtitle, summary, method, each section by id). MEMOISED and never // re-rendered once mounted: the reader wraps notes' quotes in s by // editing this DOM directly (components/articles/anchorDom.ts), which is safe // only because React has no reason to touch it again. Everything that changes // -- which citation is open, which note is selected -- goes through the // context below, whose value never changes. // // A citation is a button (its label, which is part of the text) and its // number (which is not: `data-anchor-skip`). Clicking it opens the evidence // panel; nothing here links out of umtool. export type ArticleActions = { openCite: (id: string) => void; noteSection: (section: string, title: string) => void; }; export const ArticleActionsContext = createContext(null); const CitationsContext = createContext>>({}); const CITE_SCHEME = "cite:"; function CiteLink({ href, children }: AnchorHTMLAttributes) { const actions = useContext(ArticleActionsContext); const citations = useContext(CitationsContext); const id = href?.startsWith(CITE_SCHEME) ? href.slice(CITE_SCHEME.length).trim() : null; if (id === null) { return ( {children} ); } const c = citations[id]; return ( <> {c?.number ?? "?"} ); } // markdown-to-jsx's elements carry common's class names, which umtool's // stylesheet does not have; these descendant rules are umtool's. const MD = "text-[14px] leading-relaxed text-[var(--color-text)] [&_p]:my-2.5 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5 [&_li]:my-1 [&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-[var(--color-line)] [&_blockquote]:pl-3 [&_blockquote]:text-[var(--color-dim)] [&_strong]:font-semibold [&_em]:italic [&_h3]:mt-4 [&_h3]:font-semibold [&_h4]:mt-3 [&_h4]:font-semibold [&_code]:font-mono [&_code]:text-[12px] [&_hr]:my-4 [&_hr]:border-[var(--color-line)]"; // The article's citation map, for markdown rendered outside the body (the // slides): its numbers, and its quotes on hover. export function ArticleCitations({ citations, children }: { citations: Readonly>; children: ReactNode }) { return {children}; } export function Md({ text }: { text: string }) { return ( {text} ); } function NoteButton({ section, title }: { section: string; title: string }) { const actions = useContext(ArticleActionsContext); return ( ); } function ArticleBodyInner({ view, video }: { view: ReportPageView; video?: ReactNode }) { return (
{view.series &&
{view.series}
}

{view.title}

{view.subtitle && (

{view.subtitle}

)}
{/* The report's own video, under its title as the published page has it. Outside every data-block, so it is never part of a quote. */} {video} {view.summary && (
summary
)} {view.sections.map((s) => (

{s.title}

{s.body && } {s.claims.map((cl) => (
{cl.title &&
{cl.title}
}
{cl.verdict && ( {view.verdicts?.[cl.verdict]?.label ?? cl.verdict} )}
{cl.findings && } {cl.citations.length > 0 && (
{cl.citations.map((id) => ( ))}
)}
))}
))} {view.method && (
method
)}
); } function CiteChip({ id }: { id: string }) { const actions = useContext(ArticleActionsContext); const c = useContext(CitationsContext)[id]; return ( ); } const ArticleBody = memo(ArticleBodyInner, (a, b) => a.view === b.view && a.video === b.video); export default ArticleBody;