import React from 'react'; import { MODES } from '../lib/modes.js'; import { NoteBody } from './MarginNote.jsx'; // FigurePopover — popover for a figure mention click. // Chrome cloned from CitePopover: fixed position near click, viewport clamping, // flip above when near bottom, backdrop click + Esc + × close. // Width: min(480px, 92vw). // // Props: // fig — { id, label, caption, page, hasImage } // n — figure number string (from the mention, e.g. "2") // pdfHash — sha256 hex string for image URL construction // pos — { x, y } click position // commentary — full commentary map from App // generating — full generating map from App // onGenerate — (figId, modeId) => void // onClose — () => void export default function FigurePopover({ fig, n, pdfHash, pos, commentary, generating, onGenerate, onClose }) { const ref = React.useRef(null); const [mode, setMode] = React.useState('history'); const [imgError, setImgError] = React.useState(false); const key = `${fig.id}:${mode}`; const entry = commentary[key]; const isGenerating = !!generating[key]; React.useLayoutEffect(() => { const el = ref.current; if (!el) return; const r = el.getBoundingClientRect(); let x = pos.x, y = pos.y + 10; if (x + r.width > window.innerWidth - 16) x = window.innerWidth - r.width - 16; if (y + r.height > window.innerHeight - 16) y = pos.y - r.height - 10; el.style.left = Math.max(8, x) + 'px'; el.style.top = Math.max(8, y) + 'px'; el.style.opacity = 1; }, [fig, pos]); React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); function handleDotClick(modeId) { setMode(modeId); if (!commentary[`${fig.id}:${modeId}`]) { onGenerate(fig.id, modeId); } } function handleRegenerate() { onGenerate(fig.id, mode); } const imgSrc = `/api/figures/${pdfHash}/${fig.id}.png`; return (