export type Author = { name: string; url?: string; affiliationIndices?: number[]; }; export interface HeroProps { title: string; titleRaw?: string; description?: string; authors?: Array< string | { name: string; url?: string; affiliationIndices?: number[] } >; affiliations?: Array<{ id: number; name: string; url?: string }>; affiliation?: string; published?: string; doi?: string; pdfProOnly?: boolean; showPdf?: boolean; links?: Array<{ label: string; url: string; icon?: string }>; } export 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); } export function stripHtml(text: string): string { return String(text || "").replace(/<[^>]*>/g, ""); } export 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" ); } export const LINK_ICONS: Record = { github: ``, paper: ``, arxiv: ``, code: ``, demo: ``, data: ``, dataset: ``, model: ``, video: ``, blog: ``, }; export function getLinkIcon(label: string, icon?: string): string | null { if (icon && LINK_ICONS[icon.toLowerCase()]) return LINK_ICONS[icon.toLowerCase()]; const key = label.toLowerCase().trim(); for (const [keyword, svg] of Object.entries(LINK_ICONS)) { if (key === keyword || key.includes(keyword)) return svg; } return null; }