"use client"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useRef, useTransition } from "react"; import { normalizeNoteBody } from "./normalize-body"; import { sanitizeRichTextHtml } from "./sanitize"; import "./rich-text.css"; type ToggleHandler = (taskIndex: number, checked: boolean) => Promise; type Props = { html: string; interactiveChecklists?: boolean; onToggleChecklist?: ToggleHandler; className?: string; clampLines?: number; }; export function RichTextContent({ html, interactiveChecklists = false, onToggleChecklist, className, clampLines, }: Props) { const router = useRouter(); const containerRef = useRef(null); const [isPending, startTransition] = useTransition(); const safeHtml = useMemo(() => { const normalized = normalizeNoteBody(html); return sanitizeRichTextHtml(normalized); }, [html]); useEffect(() => { const root = containerRef.current; if (!root || !interactiveChecklists || !onToggleChecklist) return; const items = [...root.querySelectorAll('li[data-type="taskItem"]')]; for (const [index, item] of items.entries()) { const checked = item.getAttribute("data-checked") === "true"; let label = item.querySelector("label"); if (!label) { label = document.createElement("label"); item.insertBefore(label, item.firstChild); } let input = label.querySelector('input[type="checkbox"]'); if (!input) { input = document.createElement("input"); input.type = "checkbox"; label.prepend(input); } input.checked = checked; input.disabled = isPending; input.onclick = (event) => { event.preventDefault(); event.stopPropagation(); const nextChecked = !input!.checked; startTransition(async () => { await onToggleChecklist(index, nextChecked); router.refresh(); }); }; } }, [safeHtml, interactiveChecklists, onToggleChecklist, isPending, router]); if (!safeHtml) { return

No body text.

; } return (
); }