import { useMemo } from 'react' import { Link, useParams } from 'react-router-dom' import DOMPurify from 'dompurify' import PublicLayout from '../../components/PublicLayout.jsx' import { Loading, ErrorState } from '../../components/PageState.jsx' import { useAsync } from '../../lib/useAsync.js' import { longDate } from '../../lib/format.js' import { api } from '../../api/client.js' function slugify(text) { return text .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)/g, '') } // Parse the stored body HTML: sanitize (defense in depth — the server also // sanitizes on save), assign ids to

headings and collect a TOC, and mark // internal links to pages that don't exist as "red links". function buildArticle(body, missing) { if (!body) return { html: '', toc: [] } if (typeof window === 'undefined' || !window.DOMParser) return { html: '', toc: [] } const safe = DOMPurify.sanitize(body) const doc = new DOMParser().parseFromString(safe, 'text/html') const toc = [] doc.querySelectorAll('h2').forEach((h, i) => { const id = slugify(h.textContent || '') || `section-${i}` h.id = id toc.push({ id, label: h.textContent }) }) doc.querySelectorAll('a[href^="/wiki/"]').forEach((a) => { const target = a.getAttribute('href').replace(/^\/wiki\//, '').replace(/[#?].*$/, '') a.removeAttribute('target') // internal links stay in-app if (missing.has(target)) { a.classList.add('wiki-red-link') a.setAttribute('title', 'This page does not exist yet') } }) return { html: doc.body.innerHTML, toc } } export default function WikiArticle() { const { slug } = useParams() const { loading, error, data: page } = useAsync(() => api.wikiPage(slug), [slug]) const missing = useMemo(() => new Set(page?.missing_links || []), [page]) const { html, toc } = useMemo(() => buildArticle(page?.body, missing), [page, missing]) return (
{loading && } {error && ( )} {page && (
{toc.length > 0 && ( )}

Wiki {page.category_title && ( <> / {page.category_title} )} / {page.title}

{page.title}

Last updated {longDate(page.updated_at) || '—'}

{page.tags && page.tags.length > 0 && (
{page.tags.map((t) => ( #{t.label} ))}
)}
{html ? (
) : (

This page has no content yet.

)} {page.backlinks && page.backlinks.length > 0 && (

Linked from

    {page.backlinks.map((b) => (
  • {b.title}
  • ))}
)}
)}
) }