"use client"; import { useState, useRef, useCallback, useEffect } from "react"; export default function SplitView({ paragraphs, classifications, enriched }) { const leftRef = useRef(null); const rightRef = useRef(null); const [syncing, setSyncing] = useState(false); const [hoveredIndex, setHoveredIndex] = useState(null); // Build a lookup: paragraph index -> classification const classMap = new Map(); if (classifications && classifications.length > 0) { for (const c of classifications) { classMap.set(c.index, c); } } // Sync scroll between panels const handleScroll = useCallback( (source) => { if (syncing) return; setSyncing(true); const sourceEl = source === "left" ? leftRef.current : rightRef.current; const targetEl = source === "left" ? rightRef.current : leftRef.current; if (sourceEl && targetEl) { const ratio = sourceEl.scrollTop / (sourceEl.scrollHeight - sourceEl.clientHeight || 1); targetEl.scrollTop = ratio * (targetEl.scrollHeight - targetEl.clientHeight); } requestAnimationFrame(() => setSyncing(false)); }, [syncing] ); // Scroll to a paragraph on click const scrollToIndex = useCallback((index, panel) => { const ref = panel === "left" ? leftRef : rightRef; const el = ref.current?.querySelector(`[data-para-index="${index}"]`); if (el) { el.scrollIntoView({ behavior: "smooth", block: "center" }); } }, []); return (
{/* Left panel: Original document view */}
Original Document
handleScroll("left")} style={{ background: "var(--bg-secondary)" }} > {paragraphs.map((p) => (
setHoveredIndex(p.index)} onMouseLeave={() => setHoveredIndex(null)} > {p.text ? (

{p.index} {p.text}

) : (

{p.index} (empty)

)} {p.fontSizePt && ( {p.fontSizePt}pt {p.isBold ? " • bold" : ""} )}
))}
{/* Right panel: Enriched view with toggle controls */}
Enriched Document {enriched && ( Click checkboxes to toggle headings )}
handleScroll("right")} style={{ background: "var(--bg-secondary)" }} > {!enriched ? (

Click "Enrich with AI" to start

The AI will detect which paragraphs are headings

) : ( )}
); } function EnrichedParagraphs({ paragraphs, classifications, classMap, hoveredIndex, setHoveredIndex, }) { // We maintain local state for checkbox toggling // This lets users toggle headings before clicking "Apply Changes" const [localClassifications, setLocalClassifications] = useState(classifications); // Sync when parent classifications change (after enrich or apply) useEffect(() => { setLocalClassifications(classifications); }, [classifications]); const localClassMap = new Map(); for (const c of localClassifications) { localClassMap.set(c.index, c); } const toggleHeading = (index) => { setLocalClassifications((prev) => prev.map((c) => c.index === index ? { ...c, isHeading: !c.isHeading } : c ) ); // Also update the parent's classifications directly so "Apply Changes" picks them up const existing = classMap.get(index); if (existing) { existing.isHeading = !existing.isHeading; } }; return ( <> {paragraphs.map((p) => { const cls = localClassMap.get(p.index); const isHeading = cls?.isHeading || false; return (
setHoveredIndex(p.index)} onMouseLeave={() => setHoveredIndex(null)} > toggleHeading(p.index)} title={isHeading ? "Unmark as heading" : "Mark as heading"} />
{p.text ? (

{p.index} {p.text}

) : (

{p.index} (empty)

)} {isHeading && ( ✦ Heading — font size will be increased )}
); })} ); }