import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import DOMPurify from 'dompurify';
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 { buildGreekDefinitionHtml, externalLookupLinks, speakOriginalWord } from '../utils/wordStudy.js';
import ProjectHeader from '../components/ProjectHeader.jsx';
import {
STUDY_TABS,
COMMENTARY_OPTIONS,
NT_BOOK_NUMBER,
formatChunkReference,
getChunkVerseEntries,
CrossRefChip,
} from '../utils/bibleUtils.jsx';
export default function StudyPage() {
const { project, allChunks, projectIndex, updateProject, updateChunk } = useApp();
// Short-lived status line (word suggestions, DOCX import, 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 selectedChunkChapterIndex = selectedChunk
? project.chapters.findIndex((ch) => ch.chunks.some((c) => c.id === selectedChunk.id))
: -1;
const selectedChunkChapter = selectedChunkChapterIndex >= 0
? project.chapters[selectedChunkChapterIndex]
: null;
const selectedChunkVerses = selectedChunk
? getChunkVerseEntries(project, selectedChunkChapterIndex, selectedChunk)
: [];
const selectedChunkGlobalIndex = allChunks.findIndex((c) => c.id === project?.selectedChunkId);
const [studyLayout, setStudyLayout] = useState(() => {
const saved = localStorage.getItem('studyLayout');
return saved === 'stacked' ? 'stacked' : 'split'; // 'annotate' (Draw) was removed
}); // 'stacked' | 'split'
const [activeStudyTab, setActiveStudyTab] = useState(
() => localStorage.getItem('activeStudyTab') || 'notes',
);
const [mobileStudyTab, setMobileStudyTab] = useState('scripture');
useEffect(() => {
localStorage.setItem('studyLayout', studyLayout);
}, [studyLayout]);
useEffect(() => {
localStorage.setItem('activeStudyTab', activeStudyTab);
}, [activeStudyTab]);
const [collapsedSections, setCollapsedSections] = useState({});
const {
commentarySource, setCommentarySource,
commentaryData,
commentaryLoading,
commentaryError,
interlinearData,
interlinearLoading,
interlinearError,
} = useStudyResources({ project, selectedChunk, selectedChunkChapterIndex, collapsedSections });
const {
crossRefInput, setCrossRefInput,
suggestingCrossRefs,
crossRefSuggestions,
addCrossRef,
removeCrossRef,
suggestCrossRefsForChunk,
addSuggestedCrossRef,
loadVerseText,
} = useCrossRefs({ project, allChunks, selectedChunk, updateChunk, notify });
const {
suggestingGreekForChunkId,
suggestingHebrewForChunkId,
suggestModal, setSuggestModal,
suggestSelection, setSuggestSelection,
updateChunkWord,
addGreekWord,
removeGreekWord,
lookupWord,
suggestGreekWordsForChunk,
suggestHebrewWordsForChunk,
confirmSuggestWords,
} = useWordStudy({ project, allChunks, updateProject, notify });
const [tagInput, setTagInput] = useState('');
const addTag = (chunkId) => {
const tag = tagInput.trim();
if (!tag) return;
const existing = selectedChunk?.tags ?? [];
if (existing.some((t) => t.toLowerCase() === tag.toLowerCase())) {
setTagInput('');
return;
}
updateChunk(chunkId, { tags: [...existing, tag] });
setTagInput('');
};
const removeTag = (chunkId, tag) => {
updateChunk(chunkId, {
tags: (selectedChunk?.tags ?? []).filter((t) => t !== tag),
});
};
useEffect(() => {
setTagInput('');
}, [selectedChunk?.id]);
const importFinalScriptDocx = async (chunkId, file) => {
if (!file) return;
try {
const arrayBuffer = await file.arrayBuffer();
const { default: mammoth } = await import('mammoth');
const { value } = await mammoth.extractRawText({ arrayBuffer });
updateChunk(chunkId, { finalScript: value.trim() });
notify('Imported script from DOCX.', 1800);
} catch {
notify('Could not read that DOCX file.', 1800);
}
};
const goToPreviousChunk = () => {
if (selectedChunkGlobalIndex <= 0) return;
updateProject((current) => ({
...current,
selectedChunkId: allChunks[selectedChunkGlobalIndex - 1].id,
}));
};
const goToNextChunk = () => {
if (selectedChunkGlobalIndex < 0 || selectedChunkGlobalIndex >= allChunks.length - 1) return;
updateProject((current) => ({
...current,
selectedChunkId: allChunks[selectedChunkGlobalIndex + 1].id,
}));
};
// Keyboard shortcuts: arrow keys navigate chunks on the study page, Esc closes modals
useEffect(() => {
const handleKeyDown = (event) => {
const tag = event.target?.tagName;
const isEditable = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || event.target?.isContentEditable;
if (event.key === 'Escape') {
if (suggestModal) {
setSuggestModal(null);
setSuggestSelection(new Set());
}
return;
}
if (isEditable || suggestModal) return;
if (event.key === 'ArrowLeft') {
event.preventDefault();
goToPreviousChunk();
} else if (event.key === 'ArrowRight') {
event.preventDefault();
goToNextChunk();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [suggestModal, selectedChunkGlobalIndex, allChunks]);
return (
Bible Study Project
{project?.title ?? ''}
{selectedChunk && selectedChunkChapterIndex >= 0 && (
{formatChunkReference(project, selectedChunkChapterIndex, selectedChunk, '–')}
)}
{/* Sidebar */}
{/* Chunk editor */}
Chunk editor
{selectedChunk
? `Section ${formatChunkReference(project, selectedChunkChapterIndex, selectedChunk, '–')}`
: 'Select a chunk'}
{[
{ id: 'stacked', label: '☰ Stacked' },
{ id: 'split', label: '◫ Split' },
].map((m) => (
setStudyLayout(m.id)}
className={`px-3 py-2 text-sm font-semibold transition ${
studyLayout === m.id
? 'bg-slate-900 text-white'
: 'bg-white text-slate-700 hover:bg-slate-50'
}`}
>
{m.label}
))}
{/* Mobile tab bar */}
{[
{ id: 'scripture', label: 'Scripture' },
{ id: 'notes', label: 'Notes' },
{ id: 'crossRefs', label: 'Refs' },
{ id: 'wordStudy', label: 'Words' },
{ id: 'commentary', label: 'Commentary' },
{ id: 'script', label: 'Script' },
].map((tab) => (
setMobileStudyTab(tab.id)}
className={`shrink-0 border-b-2 px-4 py-2.5 text-sm font-semibold transition ${
mobileStudyTab === tab.id
? 'border-slate-900 text-slate-900'
: 'border-transparent text-slate-500 hover:text-slate-700'
}`}
>
{tab.label}
))}
{selectedChunk ? (
{/* Scripture */}
Scripture
Read-only passage for the selected chunk.
{formatChunkReference(project, selectedChunkChapterIndex, selectedChunk, '–')}
{selectedChunkVerses
.map((verse) => (
{verse.chapter}:{verse.number}. {verse.text}
))}
{/* Interlinear */}
setCollapsedSections((c) => ({ ...c, interlinear: !c.interlinear }))}
className="mb-2 flex w-full items-center justify-between gap-2 text-left"
>
Interlinear
{collapsedSections.interlinear ? '▸' : '▾'}
{!collapsedSections.interlinear && (
{interlinearLoading &&
Loading interlinear text...
}
{interlinearError &&
{interlinearError}
}
{!interlinearLoading && !interlinearError && interlinearData
&& Array.from(
{ length: selectedChunk.endVerse - selectedChunk.startVerse + 1 },
(_, i) => selectedChunk.startVerse + i,
)
.filter((num) => interlinearData[String(num)])
.map((num) => (
{selectedChunkChapter.chapter}:{num}. {' '}
{interlinearData[String(num)].map((w, i) => (
{w.o}
{w.g || '—'}
))}
))}
)}
{/* Right column (study panels) */}
{studyLayout === 'split' && (
{STUDY_TABS.map((tab) => (
setActiveStudyTab(tab.id)}
className={`rounded-2xl px-4 py-2 text-sm font-semibold transition ${
activeStudyTab === tab.id
? 'bg-slate-900 text-white'
: 'text-slate-600 hover:bg-slate-100'
}`}
>
{tab.label}
))}
)}
{/* Session metadata */}
{/* Background / general notes */}
setCollapsedSections((c) => ({ ...c, generalNotes: !c.generalNotes }))}
className="flex w-full items-center justify-between gap-2 text-left"
>
Background / General Notes
Context, history, authorship, themes for this chunk.
{collapsedSections.generalNotes ? '▸' : '▾'}
{!collapsedSections.generalNotes && (
{/* OIA Notes */}
setCollapsedSections((c) => ({ ...c, oia: !c.oia }))}
className="flex w-full items-center justify-between gap-2 text-left"
>
Study Notes (OIA)
Observation · Interpretation · Application
{collapsedSections.oia ? '▸' : '▾'}
{!collapsedSections.oia && [
{
field: 'observation',
label: 'Observation',
placeholder: "What does the text actually say?\n• Who is speaking, and to whom?\n• What key words or phrases repeat?\n• What's the tone, structure, or literary style?",
},
{
field: 'interpretation',
label: 'Interpretation',
placeholder: "What did this mean to its original audience?\n• What's the historical/cultural context?\n• How does it fit the surrounding argument?\n• What does it reveal about God's character?",
},
{
field: 'application',
label: 'Application',
placeholder: 'How should this shape your life today?\n• What attitude or action does this call for?\n• Is there a promise to trust or a warning to heed?\n• Who could you share this with?',
},
].map(({ field, label, placeholder }) => (
{label}
))}
{/* Tags */}
Tags
Label this chunk by topic so you can search across studies later.
{(selectedChunk.tags ?? []).map((tag) => (
{tag}
removeTag(selectedChunk.id, tag)}
className="text-indigo-400 transition hover:text-indigo-700"
aria-label={`Remove tag ${tag}`}
>
×
))}
setTagInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') { e.preventDefault(); addTag(selectedChunk.id); }
}}
placeholder="Add a tag and press Enter"
list="tag-suggestions"
className="min-w-[10rem] flex-1 rounded-2xl border border-slate-300 bg-white px-3 py-1.5 text-sm text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
{(() => {
const used = new Set((selectedChunk.tags ?? []).map((t) => t.toLowerCase()));
const allTags = Array.from(
new Set([
...projectIndex.flatMap((entry) => entry.tags ?? []),
...(project?.chapters ?? []).flatMap((ch) => ch.chunks ?? []).flatMap((c) => c.tags ?? []),
]),
)
.filter((t) => !used.has(t.toLowerCase()))
.sort((a, b) => a.localeCompare(b));
if (allTags.length === 0) return null;
return (
Suggestions:
{allTags.map((tag) => (
updateChunk(selectedChunk.id, { tags: [...(selectedChunk.tags ?? []), tag] })}
className="rounded-full border border-indigo-200 px-3 py-1 text-xs font-semibold text-indigo-600 transition hover:bg-indigo-50"
>
+ {tag}
))}
);
})()}
{Array.from(
new Set([
...projectIndex.flatMap((entry) => entry.tags ?? []),
...(project?.chapters ?? []).flatMap((ch) => ch.chunks ?? []).flatMap((c) => c.tags ?? []),
]),
).map((tag) => (
))}
{/* Cross-references */}
setCollapsedSections((c) => ({ ...c, crossRefs: !c.crossRefs }))}
className="mb-3 flex w-full items-center justify-between gap-2 text-left"
>
Cross-References
{collapsedSections.crossRefs ? '▸' : '▾'}
{!collapsedSections.crossRefs && <>
setCrossRefInput(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addCrossRef(selectedChunk.id); } }}
placeholder="e.g. John 1:1 or Rom 3:23"
className="flex-1 rounded-2xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
addCrossRef(selectedChunk.id)}
className="rounded-2xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-slate-800"
>
Add
suggestCrossRefsForChunk(selectedChunk.id)}
disabled={suggestingCrossRefs}
className="mt-2 rounded-2xl border border-amber-300 bg-amber-50 px-4 py-2 text-sm font-semibold text-amber-700 transition hover:bg-amber-100 disabled:cursor-not-allowed disabled:opacity-60"
>
{suggestingCrossRefs ? 'Suggesting...' : '✨ Suggest from passage'}
{crossRefSuggestions.length > 0 && (
{crossRefSuggestions.map((ref) => (
addSuggestedCrossRef(selectedChunk.id, ref)}
className="inline-flex items-center gap-1 rounded-full border border-amber-300 bg-white px-3 py-1 text-sm text-amber-700 transition hover:bg-amber-100"
>
+ {ref}
))}
)}
{(selectedChunk.crossReferences ?? []).length > 0 && (
{selectedChunk.crossReferences.map((ref) => (
removeCrossRef(selectedChunk.id, ref)}
loadVerseText={loadVerseText}
/>
))}
)}
>}
{/* Greek/Hebrew word studies */}
setCollapsedSections((c) => ({ ...c, greek: !c.greek }))}
className="mb-5 flex w-full items-center justify-between gap-4 text-left"
>
Word Studies (Greek/Hebrew)
Add lexical notes, look up Strong's entries, and suggest words from the passage.
{collapsedSections.greek ? '▸' : '▾'}
{!collapsedSections.greek && <>
suggestGreekWordsForChunk(selectedChunk.id)}
disabled={suggestingGreekForChunkId === selectedChunk.id}
className="rounded-full bg-amber-100 px-4 py-2 text-sm font-semibold text-amber-800 border border-amber-300 transition hover:bg-amber-200 disabled:opacity-50 disabled:cursor-not-allowed"
title="Auto-populate Greek words that actually appear in this passage"
>
{suggestingGreekForChunkId === selectedChunk.id ? 'Suggesting…' : '✦ Suggest from passage'}
suggestHebrewWordsForChunk(selectedChunk.id)}
disabled={suggestingHebrewForChunkId === selectedChunk.id}
className="rounded-full bg-emerald-100 px-4 py-2 text-sm font-semibold text-emerald-800 border border-emerald-300 transition hover:bg-emerald-200 disabled:opacity-50 disabled:cursor-not-allowed"
title="Suggest Hebrew words for Old Testament passages"
>
{suggestingHebrewForChunkId === selectedChunk.id ? 'Suggesting…' : '✦ Suggest Hebrew'}
addGreekWord(selectedChunk.id)}
className="rounded-full bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-slate-800"
>
Add Greek Word
{selectedChunk.greekWords.length === 0 ? (
No words yet. Add one to begin a lookup.
) : (
selectedChunk.greekWords.map((word) => (
Strong's number or English gloss
updateChunkWord(selectedChunk.id, word.id, { query: e.target.value })}
onKeyDown={(e) => {
if (e.key === 'Enter') { e.preventDefault(); lookupWord(selectedChunk.id, word.id, 'greek'); }
}}
placeholder="G4102, H7225, 4102, or a word"
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
lookupWord(selectedChunk.id, word.id, 'greek')}
className="inline-flex items-center justify-center rounded-2xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-slate-800"
>
{word.loading ? 'Looking up...' : 'Look Up Greek'}
lookupWord(selectedChunk.id, word.id, 'hebrew')}
className="inline-flex items-center justify-center rounded-2xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 transition hover:border-slate-400"
>
{word.loading ? 'Looking up...' : 'Look Up Hebrew'}
removeGreekWord(selectedChunk.id, word.id)}
className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-2 text-sm font-semibold text-rose-700 transition hover:bg-rose-100"
>
Delete
{word.strongNumber ? (
{word.strongNumber}
{(word.shortDefinition || word.englishGloss) && (
{word.shortDefinition || word.englishGloss}
)}
) : null}
{word.strongNumber?.startsWith('H') ? 'Hebrew word' : 'Greek word'}
{word.lexeme && (
speakOriginalWord(word.lexeme, word.strongNumber)}
className="rounded-lg bg-sky-100 px-2 py-0.5 text-[11px] font-medium text-sky-700 hover:bg-sky-200 transition"
title="Hear pronunciation"
>
🔊 Speak
)}
updateChunkWord(selectedChunk.id, word.id, { lexeme: e.target.value })}
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
Transliteration
updateChunkWord(selectedChunk.id, word.id, { transliteration: e.target.value })}
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
Part of speech
updateChunkWord(selectedChunk.id, word.id, { partOfSpeech: e.target.value })}
placeholder="e.g. Noun"
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
English word
updateChunkWord(selectedChunk.id, word.id, { englishGloss: e.target.value })}
placeholder="e.g. grace"
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
Short definition
updateChunkWord(selectedChunk.id, word.id, { shortDefinition: e.target.value })}
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
/>
{word.definitionHtml ? (
Extended definition
) : null}
{(word.shortDefinition === 'No definition found.' || word.shortDefinition === 'Lookup failed.') && !word.lexeme && !word.definitionHtml && word.query.trim() ? (
Not found in BDBT — look it up manually:
{externalLookupLinks(word.query).map(({ label, url }) => (
{label} ↗
))}
navigator.clipboard.writeText(word.query.trim())}
className="rounded-lg border border-amber-300 bg-white px-3 py-1 text-xs font-medium text-amber-900 transition hover:bg-amber-100"
>
Copy "{word.query.trim()}"
) : null}
))
)}
>}
{/* Commentary */}
setCollapsedSections((c) => ({ ...c, commentary: !c.commentary }))}
className="mb-3 flex w-full items-center justify-between gap-2 text-left"
>
Commentary
{collapsedSections.commentary ? '▸' : '▾'}
{!collapsedSections.commentary && <>
setCommentarySource(e.target.value)}
className="mb-3 block w-full rounded-2xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
>
{COMMENTARY_OPTIONS.map((c) => (
{c.name}
))}
{commentaryLoading &&
Loading commentary...
}
{commentaryError &&
{commentaryError}
}
{!commentaryLoading && !commentaryError && commentaryData && (() => {
const verses = (commentaryData.chapter?.content ?? []).filter(
(item) => item.type === 'verse'
&& item.number >= selectedChunk.startVerse
&& item.number <= selectedChunk.endVerse,
);
if (verses.length === 0) {
return
No commentary found for this verse range.
;
}
return (
{verses.map((v) => (
Verse {v.number}
{(v.content ?? []).map((p, i) => (
{
e.preventDefault();
const sourceName = COMMENTARY_OPTIONS.find((c) => c.id === commentarySource)?.name ?? commentarySource;
const ref = formatChunkReference(project, selectedChunkChapterIndex, selectedChunk, '–');
const citation = `${p} (${sourceName}, ${ref}:${v.number})`;
const existing = selectedChunk.generalNotes ?? '';
const next = existing ? `${existing}\n\n${citation}` : citation;
updateChunk(selectedChunk.id, { generalNotes: next });
}}
>
{p}
))}
))}
);
})()}
>}
{/* Final episode script */}
setCollapsedSections((c) => ({ ...c, finalScript: !c.finalScript }))}
className="flex w-full items-center justify-between gap-2 text-left"
>
Final Script (optional)
Paste the finished script for this chunk once Claude has helped you write it — keeps the project as a complete archive. Useful for a podcast, sermon, or any spoken teaching, not required for personal study.
{collapsedSections.finalScript ? '▸' : '▾'}
{!collapsedSections.finalScript && (
<>
⬆ Upload .docx
{
const file = e.target.files?.[0];
importFinalScriptDocx(selectedChunk.id, file);
e.target.value = '';
}}
/>
Replaces the text below with the document's text.
) : (
Add a chunk from ← Back to Chunks to start studying.
Select a chunk from the left panel to edit its notes and Greek studies.
)}
{/* Prev / Next — fixed bottom bar, stays visible while scrolling long chunks */}
{selectedChunk && (
← Previous Chunk
{`Chunk ${selectedChunkGlobalIndex + 1} of ${allChunks.length}`}
= allChunks.length - 1}
className="rounded-2xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 transition hover:border-slate-400 disabled:cursor-not-allowed disabled:opacity-40"
>
Next Chunk →
)}
{/* Greek word picker modal */}
{suggestModal && createPortal(
{ if (e.target === e.currentTarget) { setSuggestModal(null); setSuggestSelection(new Set()); } }}
>
Select {suggestModal.language === 'hebrew' ? 'Hebrew' : 'Greek'} words to add
{suggestModal.helperText || "Content words are pre-checked. Uncheck any you don't need."}
{ setSuggestModal(null); setSuggestSelection(new Set()); }}
className="text-slate-400 hover:text-slate-600 text-xl leading-none"
>×
setSuggestSelection(new Set(suggestModal.words.map((w) => w.strongKey)))}
className="text-xs text-sky-600 hover:underline"
>Select all
·
setSuggestSelection(new Set())}
className="text-xs text-sky-600 hover:underline"
>Select none
{suggestSelection.size} selected
{suggestModal.words.map((w) => (
{
setSuggestSelection((prev) => {
const next = new Set(prev);
e.target.checked ? next.add(w.strongKey) : next.delete(w.strongKey);
return next;
});
}}
className="h-4 w-4 shrink-0 rounded border-slate-300 accent-slate-900"
/>
{w.def || w.strongKey}
{w.lexeme}
{w.translit && {w.translit} }
{w.strongKey}
{w.entry && (w.entry.strongs_def || w.entry.kjv_def || w.entry.derivation) && (
e.stopPropagation()}
>
Definition
)}
))}
{ setSuggestModal(null); setSuggestSelection(new Set()); }}
className="flex-1 rounded-xl border border-slate-200 py-2 text-sm font-semibold text-slate-600 hover:bg-slate-50"
>Cancel
Add {suggestSelection.size > 0 ? `${suggestSelection.size} word${suggestSelection.size > 1 ? 's' : ''}` : 'words'}
,
document.body
)}
{statusMessage && (
{statusMessage}
)}
);
}