import React from 'react'; import { MODES, modeById } from '../lib/modes.js'; // NoteBody — shared note-body state renderer (loading / streaming / error / done). // Extracted so FigurePopover can reuse the exact same state logic without copy-paste. // Props match the shape MarginNote already uses internally. // entry — commentary entry or undefined // generating — boolean // onRegenerate — () => void export function NoteBody({ entry, generating, onRegenerate }) { if (generating && !entry?.text) { return
consulting the literature
; } if (entry && entry.streaming && entry.text) { return
{entry.text}
; } if (entry && entry.error) { return (
{entry.message ? entry.message + ' — ' : 'the annotator did not answer. '}
); } if (entry) { return
{entry.text}
; } return
awaiting annotation…
; } // MarginNote — a single margin annotation card. // Differences from prototype: // 1. lit lookup comes through props (litByPara, litPapers) instead of window.ASTROPARSE_DATA. // 2. modeById imported from lib/modes.js instead of global. // Everything else (classNames, DOM structure, hover/mode/regen handlers) is identical. export default function MarginNote({ para, mode, entry, generating, hovered, onHover, onSetMode, onRegenerate, onCite, top, noteRef, litByPara, litPapers, litCount, userNote, onSaveNote }) { const m = modeById[mode]; const lit = (litByPara[para.id] || []).slice(0, litCount ?? 3).map((id) => litPapers[id]).filter(Boolean); const [noteOpen, setNoteOpen] = React.useState(false); const [draftText, setDraftText] = React.useState(userNote?.text || ''); const timerRef = React.useRef(null); React.useEffect(() => { setDraftText(userNote?.text || ''); }, [userNote?.text]); React.useEffect(() => { return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, []); const handleNoteChange = (e) => { const val = e.target.value; setDraftText(val); if (timerRef.current) clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { onSaveNote && onSaveNote(para.id, val); }, 400); }; const handleNoteBlur = () => { if (timerRef.current) clearTimeout(timerRef.current); onSaveNote && onSaveNote(para.id, draftText); if (!draftText.trim()) setNoteOpen(false); }; return (