Teaching tools: word occurrences, blanks handout, templates, series progress, book info, comments
Build and Push Docker Image / build-and-push (push) Successful in 6m41s

- Words tab: "Where else" lists every verse using a Strong's number, by book
  (new /api/strongs/:number/occurrences, indexed from the interlinear files)
- Handout: fill-in-the-blanks layout with a word bank
- Four new study templates: character, topical, word, verse by verse
- Finish: series progress (not started / studied / ready / taught)
- Background: book at a glance for all 66 books
- Chunk comments for co-teachers, merged between copies
- TODO: correct BSB audio note, tick co-teacher comments

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-27 15:44:37 -04:00
1 parent d9891ff9fe
commit a6af9bd1c6
24 files changed
+670 -9

No files matched your search

+4
View File
@@ -8,6 +8,7 @@ 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';
@@ -16,6 +17,7 @@ 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) => (
@@ -34,6 +36,7 @@ function WordCard({ word, open, onToggle, chunkId, wordStudy }) {
{open && (
<div className="space-y-3 border-t border-[var(--r-line)] px-3 py-3">
{lexicon && iq && strongs && <IqLexicon strongs={strongs} onClose={() => setLexicon(false)} />}
{where && strongs && <WordOccurrences strongs={strongs} onClose={() => setWhere(false)} />}
<label className="block text-xs text-[var(--r-sub)]">Strong’s number or English gloss
<input type="text" value={word.query} onChange={(e) => set({ query: e.target.value })}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); lookupWord(chunkId, word.id, 'greek'); } }}
@@ -43,6 +46,7 @@ function WordCard({ word, open, onToggle, chunkId, wordStudy }) {
<button type="button" onClick={() => 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'}</button>
<button type="button" onClick={() => lookupWord(chunkId, word.id, 'hebrew')} className={btn}>{word.loading ? 'Looking up...' : 'Look up Hebrew'}</button>
{iq && strongs && <button type="button" onClick={() => setLexicon((o) => !o)} aria-pressed={lexicon} className={btn} title="Strong's, BDB, LSJ and Abbott-Smith entries">Lexicons</button>}
{strongs && <button type="button" onClick={() => setWhere((o) => !o)} aria-pressed={where} className={btn} title="Every verse that uses this word, by book">Where else</button>}
{word.lexeme && <button type="button" onClick={() => speakOriginalWord(word.lexeme, word.strongNumber)} className={btn} title="Hear pronunciation">Speak</button>}
<button type="button" onClick={() => removeGreekWord(chunkId, word.id)} className={`${btn} ml-auto text-[var(--r-danger,#b42318)]`}>Delete</button>
</div>