// The Study stage: the chunk outline on the left, the passage and your // Observe / Interpret / Apply notes in the middle, and commentary, word study, // cross-references and people & places on the right — all for the selected chunk. import { useEffect, useRef, useState } from 'react'; import { useApp } from '../context/AppContext.js'; import { useCrossRefs } from '../hooks/useCrossRefs.js'; import { useStudyResources } from '../hooks/useStudyResources.js'; import { useWordStudy } from '../hooks/useWordStudy.js'; import { useResizableWidth } from '../hooks/useResizableWidth.js'; import { formatChunkReference, getChunkVerseEntries } from '../utils/bibleUtils.jsx'; import StudioShell from '../components/study/StudioShell.jsx'; import OutlineRail from '../components/study/OutlineRail.jsx'; import ScripturePane from '../components/study/ScripturePane.jsx'; import NotesPane from '../components/study/NotesPane.jsx'; import ResourcePane, { useResourceTab } from '../components/study/ResourcePane.jsx'; import SuggestWordsSheet from '../components/study/SuggestWordsSheet.jsx'; export default function StudyPage() { const { project, allChunks, projectIndex, updateProject, updateChunk, setCurrentPage, readerCommentary, setReaderCommentary } = useApp(); // Short-lived status line (word suggestions, lookups, …) const [statusMessage, setStatusMessage] = useState(''); const statusTimerRef = useRef(null); const notify = (message, ms = 2000) => { window.clearTimeout(statusTimerRef.current); setStatusMessage(message); statusTimerRef.current = window.setTimeout(() => setStatusMessage(''), ms); }; useEffect(() => () => window.clearTimeout(statusTimerRef.current), []); const selectedChunk = allChunks.find((c) => c.id === project?.selectedChunkId) ?? allChunks[0] ?? null; const chapterIndex = selectedChunk ? project.chapters.findIndex((ch) => ch.chunks.some((c) => c.id === selectedChunk.id)) : -1; const chapter = chapterIndex >= 0 ? project.chapters[chapterIndex] : null; const verses = selectedChunk ? getChunkVerseEntries(project, chapterIndex, selectedChunk) : []; const globalIndex = allChunks.findIndex((c) => c.id === selectedChunk?.id); // The right column's width can be dragged (remembered on this device) const gridRef = useRef(null); const rightPanelRef = useRef(null); const rightPanel = useResizableWidth({ storageKey: 'study-right-width', containerRef: gridRef }); const [tab, setTab] = useResourceTab(); const [openWordId, setOpenWordId] = useState(null); useEffect(() => { setOpenWordId(null); }, [selectedChunk?.id]); const resources = useStudyResources({ project, selectedChunk, selectedChunkChapterIndex: chapterIndex, collapsedSections: { commentary: tab !== 'commentary', interlinear: false }, commentarySource: readerCommentary, setCommentarySource: setReaderCommentary, }); const crossRefs = useCrossRefs({ project, allChunks, selectedChunk, updateChunk, notify }); const wordStudy = useWordStudy({ project, allChunks, updateProject, notify }); const select = (id) => updateProject((current) => ({ ...current, selectedChunkId: id })); const goPrev = () => { if (globalIndex > 0) select(allChunks[globalIndex - 1].id); }; const goNext = () => { if (globalIndex >= 0 && globalIndex < allChunks.length - 1) select(allChunks[globalIndex + 1].id); }; // Arrow keys move between chunks; Escape closes the word picker useEffect(() => { const onKey = (event) => { const tag = event.target?.tagName; const editable = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || event.target?.isContentEditable; if (event.key === 'Escape') { if (wordStudy.suggestModal) { wordStudy.setSuggestModal(null); wordStudy.setSuggestSelection(new Set()); } return; } if (editable || wordStudy.suggestModal) return; if (event.key === 'ArrowLeft') { event.preventDefault(); goPrev(); } else if (event.key === 'ArrowRight') { event.preventDefault(); goNext(); } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }); const allTags = Array.from(new Set([ ...projectIndex.flatMap((entry) => entry.tags ?? []), ...(project?.chapters ?? []).flatMap((ch) => ch.chunks ?? []).flatMap((c) => c.tags ?? []), ])).sort((a, b) => a.localeCompare(b)); const addedStrongs = new Set((selectedChunk?.greekWords ?? []).map((w) => (w.strongNumber || '').toUpperCase()).filter(Boolean)); const pickWord = async (w) => { const id = await wordStudy.addWordFromInterlinear(selectedChunk.id, w); if (id) { setTab('words'); setOpenWordId(id); } }; return (
{selectedChunk && chapter ? (

{formatChunkReference(project, chapterIndex, selectedChunk, '–')}

{`Chunk ${globalIndex + 1} of ${allChunks.length}`}

{selectedChunk.episodeTitle && (

{selectedChunk.episodeNumber ? `${selectedChunk.episodeNumber}: ` : ''}{selectedChunk.episodeTitle}

)}
) : (

Add a chunk in the Outline stage to start studying.

)}
{statusMessage && (
{statusMessage}
)}
); }