import type { Transformer } from "./types.js"; const NUMERIC_STYLES = new Set(["ieee", "vancouver"]); /** * Converts inline `` markers produced by TipTap * into anchor links pointing at the bibliography entries. * * - Numeric styles (IEEE, Vancouver) display sequential `[n]` labels. * - Author-date styles keep the original inline label (e.g. "(Smith 2020)"). * * Must run before the bibliography transformer so the `#ref-` anchors * line up with the entry ids created there. */ export const citationTransformer: Transformer = { name: "citation", apply(document, ctx) { const isNumeric = ctx.citationData ? NUMERIC_STYLES.has(ctx.citationData.style) : false; const citationKeyOrder: string[] = []; // Index bibliography entries by key so we can enrich each inline citation // with a hover tooltip showing title / journal / DOI. const entriesByKey = new Map(); for (const entry of ctx.citationData?.entries ?? []) { const entryKey = (entry?.id as string) || (entry?.key as string); if (entryKey) entriesByKey.set(entryKey, entry); } for (const span of [...document.querySelectorAll('span[data-type="citation"]')]) { const key = span.getAttribute("key") || span.getAttribute("data-key"); if (!key) continue; if (!citationKeyOrder.includes(key)) citationKeyOrder.push(key); const idx = citationKeyOrder.indexOf(key) + 1; const originalLabel = span.textContent || `[${key}]`; const displayLabel = isNumeric ? `[${idx}]` : originalLabel; const a = document.createElement("a"); a.setAttribute("href", `#ref-${key}`); a.className = "citation-inline"; a.setAttribute("id", `cite-${key}-${idx}`); a.setAttribute("title", key); a.textContent = displayLabel; const entry = entriesByKey.get(key); if (entry) { const tooltip = document.createElement("span"); tooltip.className = "pub-tooltip pub-tooltip--citation"; tooltip.setAttribute("aria-hidden", "true"); const titleEl = document.createElement("span"); titleEl.className = "pub-tooltip__title"; titleEl.textContent = (entry.title as string) || key; tooltip.appendChild(titleEl); const journalText = (entry["container-title"] as string) || ""; if (journalText) { const journal = document.createElement("span"); journal.className = "pub-tooltip__journal"; const parts: string[] = [journalText]; if (entry.volume) parts.push(String(entry.volume)); if (entry.page) parts.push(String(entry.page)); journal.textContent = parts.join(", "); tooltip.appendChild(journal); } if (entry.DOI) { const doi = document.createElement("span"); doi.className = "pub-tooltip__doi"; doi.textContent = `doi: ${entry.DOI}`; tooltip.appendChild(doi); } a.appendChild(tooltip); } span.replaceWith(a); } }, };