tfrere's picture
tfrere HF Staff
refactor(backend/publisher): transformer pipeline + shared registry + shiki highlighting
7843436
Raw
History Blame Contribute Delete
3.06 kB
import type { Transformer } from "./types.js";
const NUMERIC_STYLES = new Set(["ieee", "vancouver"]);
/**
* Converts inline `<span data-type="citation">` 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-<key>` 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<string, any>();
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);
}
},
};