// Greek/Hebrew word study for the chunk: compact cards (one open for editing at a
// time), Strong's lookup, and suggestions from the passage.
import { useState } from 'react';
import DOMPurify from 'dompurify';
import { externalLookupLinks, speakOriginalWord } from '../../utils/wordStudy.js';
import { useIqAvailable } from '../../hooks/useIq.js';
import { normalizeStrongs } from '../../utils/iqbible.js';
import IqLexicon from '../iq/IqLexicon.jsx';
import GrammarPanel from '../GrammarPanel.jsx';
import IqDictionary from '../iq/IqDictionary.jsx';
import WordOccurrences from './WordOccurrences.jsx';
const inputCls = 'mt-1 block w-full rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-2.5 py-1.5 text-sm text-[var(--r-text)] focus:outline-none focus:ring-2 focus:ring-[var(--r-line)]';
const btn = 'rounded-lg border border-[var(--r-line)] px-2.5 py-1.5 text-xs hover:bg-black/5 disabled:opacity-50';
function WordCard({ word, open, onToggle, chunkId, wordStudy }) {
const { updateChunkWord, lookupWord, removeGreekWord } = wordStudy;
const iq = useIqAvailable();
const [lexicon, setLexicon] = useState(false);
const [where, setWhere] = useState(false);
const strongs = normalizeStrongs(word.strongNumber || word.query);
const set = (patch) => updateChunkWord(chunkId, word.id, patch);
const field = (label, key, placeholder) => (
{label}
set({ [key]: e.target.value })} className={inputCls} />
);
const notFound = (word.shortDefinition === 'No definition found.' || word.shortDefinition === 'Lookup failed.') && !word.lexeme && !word.definitionHtml && word.query.trim();
return (
{word.lexeme || New word }
{[word.transliteration, word.englishGloss || word.shortDefinition].filter(Boolean).join(' · ')}
{word.strongNumber && {word.strongNumber} }
{open && (
{lexicon && iq && strongs &&
setLexicon(false)} />}
{where && strongs && setWhere(false)} />}
Strong’s number or English gloss
set({ query: e.target.value })}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); lookupWord(chunkId, word.id, 'greek'); } }}
placeholder="G4102, H7225, 4102, or a word" className={inputCls} />
lookupWord(chunkId, word.id, 'greek')} className={`${btn} bg-[var(--r-text)] text-[var(--r-bg)] hover:opacity-90`}>{word.loading ? 'Looking up...' : 'Look up Greek'}
lookupWord(chunkId, word.id, 'hebrew')} className={btn}>{word.loading ? 'Looking up...' : 'Look up Hebrew'}
{iq && strongs && setLexicon((o) => !o)} aria-pressed={lexicon} className={btn} title="Strong's, BDB, LSJ and Abbott-Smith entries">Lexicons }
{strongs && setWhere((o) => !o)} aria-pressed={where} className={btn} title="Every verse that uses this word, by book">Where else }
{word.lexeme && speakOriginalWord(word.lexeme, word.strongNumber)} className={btn} title="Hear pronunciation">Speak }
removeGreekWord(chunkId, word.id)} className={`${btn} ml-auto text-[var(--r-danger,#b42318)]`}>Delete
{field(word.strongNumber?.startsWith('H') ? 'Hebrew word' : 'Greek word', 'lexeme')}
{field('Transliteration', 'transliteration')}
{field('Part of speech', 'partOfSpeech', 'e.g. Noun')}
{field('English word', 'englishGloss', 'e.g. grace')}
{word.parsing && }
{field('Short definition', 'shortDefinition')}
{word.definitionHtml && (
Extended definition
)}
{notFound ? (
Not found in BDBT — look it up manually:
{externalLookupLinks(word.query).map(({ label, url }) => (
{label} ↗
))}
navigator.clipboard.writeText(word.query.trim())} className={btn}>Copy “{word.query.trim()}”
) : null}
)}
);
}
export default function WordStudyTab({ wordStudy, chunk, openWordId, setOpenWordId }) {
const { suggestingGreekForChunkId, suggestingHebrewForChunkId, suggestGreekWordsForChunk, suggestHebrewWordsForChunk, addGreekWord } = wordStudy;
const iq = useIqAvailable(); // the dictionaries come from IQ Bible, so they appear only when the server has a key
return (
{iq &&
}
suggestGreekWordsForChunk(chunk.id)} disabled={suggestingGreekForChunkId === chunk.id} className={btn}
title="Auto-populate Greek words that actually appear in this passage">{suggestingGreekForChunkId === chunk.id ? 'Suggesting…' : 'Suggest Greek'}
suggestHebrewWordsForChunk(chunk.id)} disabled={suggestingHebrewForChunkId === chunk.id} className={btn}
title="Suggest Hebrew words for Old Testament passages">{suggestingHebrewForChunkId === chunk.id ? 'Suggesting…' : 'Suggest Hebrew'}
setOpenWordId(addGreekWord(chunk.id))} className={`${btn} ml-auto bg-[var(--r-text)] text-[var(--r-bg)] hover:opacity-90`}>Add word
{chunk.greekWords.length === 0 && (
No words yet. Open a verse’s original words in the text and click one, or add a word to look it up.
)}
{chunk.greekWords.map((w) => (
setOpenWordId(openWordId === w.id ? null : w.id)} />
))}
);
}