--- import HtmlEmbed from "./HtmlEmbed.astro"; import bannerImage from "../content/assets/image/nanochat-banner.png"; interface Props { title: string; // may contain HTML (e.g.,
) titleRaw?: string; // plain title for slug/PDF (optional) description?: string; authors?: Array< string | { name: string; url?: string; affiliationIndices?: number[] } >; affiliations?: Array<{ id: number; name: string; url?: string }>; affiliation?: string; // legacy single affiliation published?: string; doi?: string; pdfProOnly?: boolean; // Gate PDF download to Pro users only showPdf?: boolean; // Show or hide PDF section in metadata } const { title, titleRaw, description, authors = [], affiliations = [], affiliation, published, doi, pdfProOnly = false, showPdf = true, } = Astro.props as Props; type Author = { name: string; url?: string; affiliationIndices?: number[] }; function normalizeAuthors( input: Array< | string | { name?: string; url?: string; link?: string; affiliationIndices?: number[]; } >, ): Author[] { return (Array.isArray(input) ? input : []) .map((a) => { if (typeof a === "string") { return { name: a } as Author; } const name = (a?.name ?? "").toString(); const url = (a?.url ?? a?.link) as string | undefined; const affiliationIndices = Array.isArray((a as any)?.affiliationIndices) ? (a as any).affiliationIndices : undefined; return { name, url, affiliationIndices } as Author; }) .filter((a) => a.name && a.name.trim().length > 0); } const normalizedAuthors: Author[] = normalizeAuthors(authors as any); // Determine if affiliation superscripts should be shown (only when there are multiple distinct affiliations referenced by authors) const authorAffiliationIndexSet = new Set(); for (const author of normalizedAuthors) { const indices = Array.isArray(author.affiliationIndices) ? author.affiliationIndices : []; for (const idx of indices) { if (typeof idx === "number") { authorAffiliationIndexSet.add(idx); } } } const shouldShowAffiliationSupers = authorAffiliationIndexSet.size > 1; const hasMultipleAffiliations = Array.isArray(affiliations) && affiliations.length > 1; function stripHtml(text: string): string { return String(text || "").replace(/<[^>]*>/g, ""); } function slugify(text: string): string { return ( String(text || "") .normalize("NFKD") .replace(/\p{Diacritic}+/gu, "") .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 120) || "article" ); } const pdfBase = titleRaw ? stripHtml(titleRaw) : stripHtml(title); const pdfFilename = `${slugify(pdfBase)}.pdf`; ---

Banner {description &&

{description}

}

{ normalizedAuthors.length > 0 && (

Author{normalizedAuthors.length > 1 ? "s" : ""}

    {normalizedAuthors.map((a, i) => { const supers = shouldShowAffiliationSupers && Array.isArray(a.affiliationIndices) && a.affiliationIndices.length ? ( {a.affiliationIndices.join(", ")} ) : null; return (
  • {a.url ? {a.name} : a.name}{supers}{i < normalizedAuthors.length - 1 && }
  • ); })}
) } { Array.isArray(affiliations) && affiliations.length > 0 && (

Affiliation{affiliations.length > 1 ? "s" : ""}

{hasMultipleAffiliations ? (
    {affiliations.map((af) => (
  1. {af.url ? ( {af.name} ) : ( af.name )}
  2. ))}
) : (

{affiliations[0]?.url ? ( {affiliations[0].name} ) : ( affiliations[0]?.name )}

)}
) } { (!affiliations || affiliations.length === 0) && affiliation && (

Affiliation

{affiliation}

) } { published && (

Published

{published}

) } {showPdf && (

PDF

)}
{showPdf && ( )}