Fold the Study notes/resources halves; compare translations

- Your notes and Commentary & resources can each be folded so the other
  fills the right column (remembered on this device; never both folded).
- Compare has a Translations view: pick BSB/ESV/NET/NLT/CSB/NASB and read the
  chunk verse by verse, with each translation's notice.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-14 12:48:38 -04:00
1 parent d8e522dd06
commit 76df145b1e
5 files changed
+216 -22

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
// Comparing translations of one chunk: which chapter(s) and verses to fetch, and the verses lined up by number.
import { getChunkVerseEntries } from './bibleUtils.jsx';
// -> [{ bookAbbrev, book, chapter, from, to }]: one entry per chapter the chunk covers (two when it spills over).
export function translationSpans(project, chapterIndex, chunk) {
const spans = [];
for (const v of getChunkVerseEntries(project, chapterIndex, chunk)) {
const last = spans[spans.length - 1];
if (last && last.chapter === v.chapter && last.book === v.book) { last.to = Math.max(last.to, v.number); last.from = Math.min(last.from, v.number); continue; }
const chapter = project.chapters.find((c) => c.book === v.book && String(c.chapter) === String(v.chapter));
spans.push({ bookAbbrev: chapter?.bookAbbrev, book: v.book, chapter: v.chapter, from: v.number, to: v.number });
}
return spans;
}
// byTranslation: { BSB: { verses: [{ number, text }] }, … } for the chapters in `spans`; `loaded` is keyed by
// `${chapter}` inside each translation. -> [{ key, label, texts: { BSB: 'text' | null } }] one row per verse, in order.
export function alignVerses(spans, byTranslation) {
const rows = [];
for (const span of spans) {
for (let n = span.from; n <= span.to; n += 1) {
const texts = {};
for (const [id, chapters] of Object.entries(byTranslation)) {
texts[id] = chapters?.[`${span.book}|${span.chapter}`]?.find((v) => v.number === n)?.text ?? null;
}
rows.push({ key: `${span.book}${span.chapter}:${n}`, label: spans.length > 1 ? `${span.chapter}:${n}` : String(n), texts });
}
}
return rows;
}
+29
View File
@@ -0,0 +1,29 @@
import { test, expect } from 'vitest';
import { translationSpans, alignVerses } from './compareTranslations.js';
const verses = (from, to) => Array.from({ length: to - from + 1 }, (_, i) => ({ number: from + i, text: `v${from + i}` }));
const project = { chapters: [
{ book: 'John', bookAbbrev: 'JHN', chapter: '3', verses: verses(1, 36), chunks: [] },
{ book: 'John', bookAbbrev: 'JHN', chapter: '4', verses: verses(1, 54), chunks: [] },
] };
test('a chunk inside one chapter is a single span', () => {
expect(translationSpans(project, 0, { startVerse: 16, endVerse: 18, spilloverEndVerse: null }))
.toEqual([{ bookAbbrev: 'JHN', book: 'John', chapter: '3', from: 16, to: 18 }]);
});
test('a chunk that runs into the next chapter has a span for each', () => {
const spans = translationSpans(project, 0, { startVerse: 35, endVerse: 36, spilloverEndVerse: 2 });
expect(spans.map((s) => [s.chapter, s.from, s.to])).toEqual([['3', 35, 36], ['4', 1, 2]]);
});
test('verses are lined up by number, with null where a translation lacks one', () => {
const spans = [{ book: 'John', chapter: '3', from: 16, to: 17 }];
const rows = alignVerses(spans, {
BSB: { 'John|3': [{ number: 16, text: 'a' }, { number: 17, text: 'b' }] },
NET: { 'John|3': [{ number: 16, text: 'c' }] },
});
expect(rows.map((r) => r.label)).toEqual(['16', '17']);
expect(rows[0].texts).toEqual({ BSB: 'a', NET: 'c' });
expect(rows[1].texts).toEqual({ BSB: 'b', NET: null });
});