This repository has no description
Something went wrong. Try again.
2.0 kB · 64 lines
TSX
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465// src/components/Footnotes.tsx// Server component — walks the Lexical AST for citation links.// The original Astro impl collected `.source-citation` anchors from rendered HTML.// Here we walk the AST for link nodes that carry `data-source-url` metadata.// None of the 42 imported posts use this pattern, so this renders nothing for now// but is wired up for future content.
type LexicalNode = { type: string url?: string fields?: { url?: string; 'data-source-url'?: string; [key: string]: unknown } children?: LexicalNode[] [key: string]: unknown}
interface Citation { url: string text: string}
function textOf(node: LexicalNode): string { if (node.type === 'text') return (node.text as string | undefined) ?? '' return (node.children ?? []).map(textOf).join('')}
function walkForCitations(nodes: LexicalNode[], acc: Citation[] = []): Citation[] { for (const node of nodes) { if (node.type === 'link') { const sourceUrl = (node.fields?.['data-source-url'] as string | undefined) ?? (node['data-source-url'] as string | undefined) if (sourceUrl) { acc.push({ url: sourceUrl, text: textOf(node) || sourceUrl }) } } if (node.children?.length) walkForCitations(node.children, acc) } return acc}
interface FootnotesProps { value?: { root: { children: LexicalNode[] } } | null}
export function Footnotes({ value }: FootnotesProps) { if (!value?.root?.children) return null const citations = walkForCitations(value.root.children) if (citations.length === 0) return null
return ( <aside className="border-hair mt-12 border-t pt-8"> <h2 className="mb-4 text-xl font-bold">Sources</h2> <ol className="list-inside list-decimal space-y-2 text-sm"> {citations.map((c, i) => ( <li key={i}> <a href={c.url} target="_blank" rel="noopener noreferrer" className="underline"> {c.text} </a> </li> ))} </ol> </aside> )}