---
import * as ArticleMod from '../content/article.mdx';
import Hero from '../components/Hero.astro';
import Footer from '../components/Footer.astro';
import ThemeToggle from '../components/ThemeToggle.astro';
import Seo from '../components/Seo.astro';
import TableOfContents from '../components/TableOfContents.astro';
// Default OG image served from public/
const ogDefaultUrl = '/thumb.auto.jpg';
import 'katex/dist/katex.min.css';
import '../styles/global.css';
const articleFM = (ArticleMod as any).frontmatter ?? {};
const Article = (ArticleMod as any).default;
// Clean version without
for metadata, SEO, citations
const docTitle = (articleFM?.title ?? 'Untitled article')
.replace(/
/gi, ' ')
.replace(/\\n/g, ' ')
.replace(/\n/g, ' ')
.replace(/\s+/g, ' ')
.trim();
// HTML version with
for Hero H1 display only
const docTitleHtml = (articleFM?.title ?? 'Untitled article')
.replace(/\\n/g, '
')
.replace(/\n/g, '
');
const subtitle = articleFM?.subtitle ?? '';
const description = articleFM?.description ?? '';
// Accept authors as string[] or array of objects { name, url, affiliations? }
const rawAuthors = (articleFM as any)?.authors ?? [];
type Affiliation = { id: number; name: string; url?: string };
type Author = { name: string; url?: string; affiliationIndices?: number[] };
// Normalize affiliations from frontmatter: supports strings or objects { id?, name, url? }
const rawAffils = (articleFM as any)?.affiliations ?? (articleFM as any)?.affiliation ?? [];
const normalizedAffiliations: Affiliation[] = (() => {
const seen: Map = new Map();
const list: Affiliation[] = [];
const pushUnique = (name: string, url?: string) => {
const key = `${String(name).trim()}|${url ? String(url).trim() : ''}`;
if (seen.has(key)) return seen.get(key)!;
const id = list.length + 1;
list.push({ id, name: String(name).trim(), url: url ? String(url) : undefined });
seen.set(key, id);
return id;
};
const input = Array.isArray(rawAffils) ? rawAffils : (rawAffils ? [rawAffils] : []);
for (const a of input) {
if (typeof a === 'string') {
pushUnique(a);
} else if (a && typeof a === 'object') {
const name = a.name ?? a.label ?? a.text ?? a.affiliation ?? '';
if (!String(name).trim()) continue;
const url = a.url || a.link;
// Respect provided numeric id for display stability if present and sequential; otherwise reassign
pushUnique(String(name), url ? String(url) : undefined);
}
}
return list;
})();
// Helper: ensure an affiliation exists and return its id
const ensureAffiliation = (val: any): number | undefined => {
if (val == null) return undefined;
if (typeof val === 'number' && Number.isFinite(val) && val > 0) {
return Math.floor(val);
}
const name = typeof val === 'string' ? val : (val?.name ?? val?.label ?? val?.text ?? val?.affiliation);
if (!name || !String(name).trim()) return undefined;
const existing = normalizedAffiliations.find(a => a.name === String(name).trim());
if (existing) return existing.id;
const id = normalizedAffiliations.length + 1;
normalizedAffiliations.push({ id, name: String(name).trim(), url: val?.url || val?.link });
return id;
};
// Normalize authors and map affiliations -> indices (Distill-like)
const normalizedAuthors: Author[] = (Array.isArray(rawAuthors) ? rawAuthors : [])
.map((a: any) => {
if (typeof a === 'string') {
return { name: a } as Author;
}
const name = String(a?.name || '').trim();
const url = a?.url || a?.link;
let indices: number[] | undefined = undefined;
const raw = a?.affiliations ?? a?.affiliation ?? a?.affils;
if (raw != null) {
const entries = Array.isArray(raw) ? raw : [raw];
const ids = entries.map(ensureAffiliation).filter((x): x is number => typeof x === 'number');
const unique = Array.from(new Set(ids)).sort((x, y) => x - y);
if (unique.length) indices = unique;
}
return { name, url, affiliationIndices: indices } as Author;
})
.filter((a: Author) => a.name && a.name.trim().length > 0);
const authorNames: string[] = normalizedAuthors.map(a => a.name);
const published = articleFM?.published ?? undefined;
const tags = articleFM?.tags ?? [];
// Prefer seoThumbImage from frontmatter if provided
const fmOg = articleFM?.seoThumbImage as string | undefined;
const imageAbs: string = fmOg && fmOg.startsWith('http')
? fmOg
: (Astro.site ? new URL((fmOg ?? ogDefaultUrl), Astro.site).toString() : (fmOg ?? ogDefaultUrl));
// ---- Build citation text & BibTeX from frontmatter ----
const stripHtml = (text: string) => String(text || '').replace(/<[^>]*>/g, '');
// Use docTitle (already cleaned) and strip any remaining HTML for BibTeX
const titleFlat = stripHtml(docTitle);
const extractYear = (val: string | undefined): number | undefined => {
if (!val) return undefined;
const d = new Date(val);
if (!Number.isNaN(d.getTime())) return d.getFullYear();
const m = String(val).match(/(19|20)\d{2}/);
return m ? Number(m[0]) : undefined;
};
const year = extractYear(published);
const citationAuthorsText = authorNames.join(', ');
const citationText = `${citationAuthorsText}${year ? ` (${year})` : ''}. "${titleFlat}".`;
const authorsBib = authorNames.join(' and ');
const keyAuthor = (authorNames[0] || 'article').split(/\s+/).slice(-1)[0].toLowerCase();
const keyTitle = titleFlat.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '');
const bibKey = `${keyAuthor}${year ?? ''}_${keyTitle}`;
const doi = (ArticleMod as any)?.frontmatter?.doi ? String((ArticleMod as any).frontmatter.doi) : undefined;
// Extract arXiv information from DOI if it's an arXiv URL
const extractArxivInfo = (doiStr: string | undefined) => {
if (!doiStr) return { eprint: undefined, primaryClass: undefined };
// Check if it's an arXiv URL (like https://arxiv.org/abs/2510.17269)
const arxivMatch = doiStr.match(/arxiv\.org\/(?:abs|pdf)\/(\d{4}\.\d+)(?:v\d+)?/);
if (arxivMatch) {
return {
eprint: arxivMatch[1],
primaryClass: 'cs.CV' // You mentioned primaryClass={cs.CV} in your request
};
}
return { eprint: undefined, primaryClass: undefined };
};
const { eprint: arxivEprint, primaryClass: arxivClass } = extractArxivInfo(doi);
// Build BibTeX with proper formatting
let bibtexFields = [
`title={${titleFlat}}`,
`author={${authorsBib}}`
];
if (year) bibtexFields.push(`year={${year}}`);
if (doi) bibtexFields.push(`doi={${doi}}`);
if (arxivEprint) {
bibtexFields.push(`eprint={${arxivEprint}}`);
bibtexFields.push(`archivePrefix={arXiv}`);
bibtexFields.push(`primaryClass={${arxivClass}}`);
}
const bibtex = `@misc{${bibKey},\n ${bibtexFields.join(',\n ')}\n}`;
const envCollapse = false;
const tableOfContentAutoCollapse = Boolean(
(articleFM as any)?.tableOfContentAutoCollapse ?? (articleFM as any)?.tableOfContentsAutoCollapse ?? envCollapse
);
// Licence note (HTML allowed)
const licence = (articleFM as any)?.licence ?? (articleFM as any)?.license ?? (articleFM as any)?.licenseNote;
---