Redesign the study side: Outline, Study, Script and Export stages
Build and Push Docker Image / build-and-push (push) Successful in 4m15s

Studies now move through a stage bar in the reader's themes. Outline splits
chunks by clicking between verses (join merges notes) and suggests chunks from
section headings. Study is three panes: outline, passage with an Observe /
Interpret / Apply accordion, and commentary / word study / cross-refs / people
& places for the chunk; original words are added by clicking them in the
interlinear. Script drafts an outline from notes; Export holds files and the
share link. Home, Import and the Pronunciation guide are restyled. Study
projects are BSB only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-09-29 10:13:46 -04:00
1 parent b1bc1c8ea4
commit 31abee0695
31 files changed
+1911 -2460

No files matched your search

+38
View File
@@ -46,6 +46,7 @@ export function useWordStudy({ project, allChunks, updateProject, notify }) {
}));
};
// Adds a blank word entry and returns its id (so the UI can open it for editing).
const addGreekWord = (chunkId) => {
const newWord = {
id: makeId(),
@@ -70,6 +71,42 @@ export function useWordStudy({ project, allChunks, updateProject, notify }) {
),
})),
}));
return newWord.id;
};
// Adds a fully filled-in entry for an original word picked from the interlinear
// (Strong's number, lexeme, transliteration and gloss come with the click).
const addWordFromInterlinear = async (chunkId, w) => {
const chunk = allChunks.find((c) => c.id === chunkId);
const key = String(w?.s ?? '').toUpperCase();
if (!chunk || !key) return null;
if (chunk.greekWords.some((x) => (x.strongNumber || '').toUpperCase() === key)) {
notify('That word is already in this chunk.', 1800);
return chunk.greekWords.find((x) => (x.strongNumber || '').toUpperCase() === key).id;
}
let entry = null;
try { entry = (key.startsWith('H') ? await loadHebrewDict() : await loadGreekDict())[key] ?? null; } catch { /* dictionary unavailable — keep what the click gave us */ }
const newWord = {
id: makeId(),
query: key,
strongNumber: key,
englishGloss: w.g || '',
lexeme: w.o || entry?.lemma || '',
transliteration: w.t || entry?.translit || entry?.xlit || '',
partOfSpeech: '',
shortDefinition: entry?.kjv_def || w.g || '',
definitionHtml: entry ? buildGreekDefinitionHtml(key, entry) : '',
loading: false,
};
updateProject((current) => ({
...current,
chapters: current.chapters.map((ch) => ({
...ch,
chunks: ch.chunks.map((c) => (c.id === chunkId ? { ...c, greekWords: [...c.greekWords, newWord] } : c)),
})),
}));
notify(`Added ${newWord.lexeme || key} to word study.`, 1800);
return newWord.id;
};
const removeGreekWord = (chunkId, wordId) => {
@@ -332,6 +369,7 @@ export function useWordStudy({ project, allChunks, updateProject, notify }) {
suggestSelection, setSuggestSelection,
updateChunkWord,
addGreekWord,
addWordFromInterlinear,
removeGreekWord,
lookupWord,
suggestGreekWordsForChunk,