Skip to content

Commit 215ad9e

Browse files
kckernclaude
andcommitted
fix(web): latex prints each block in its own version
LatexPresenter took one version for the whole reference (the lookup's first resolved version), so /jst.john.3.16;john.3.16 lost John 3:16 and mixed references dropped a block's verses. Live is worse: inc_latex.php read renderRaw's data, the request's version (LDS) only, so every JST verse disappeared (/jst.john.3.16 empty). Decision 2026-10-23: fix, not copy. Each block (each section of a multi-block result) now prints in the version its lookup resolved, as html and plain do; the comment that claimed PHP's rule is corrected. Built-in and traffic latex unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 385ecb6 commit 215ad9e

2 files changed

Lines changed: 40 additions & 10 deletions

File tree

‎apps/web/src/presentation/presenters/formats/latex.js‎

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,35 @@ import { renderLatex, footnoteIds } from '../../latex/LatexRenderer.js';
33
import { flag } from '../../formatOptions.js';
44

55
// latex.scripture.guide: a LaTeX body fragment (text/plain), ported from PHP inc_latex.php.
6-
// The text comes from the first requested version, as PHP renderRaw did.
6+
// Each block's text comes from the version its lookup resolved, as html and plain do. PHP read
7+
// renderRaw's data, the request's version only (LDS), and so dropped JST verses ("/jst.john.3.16"
8+
// was empty); fixed rather than copied (decision 2026-10-23).
79
export class LatexPresenter {
810
constructor(repo = latexRepository) {
911
this.repo = repo;
1012
}
1113

14+
// The version a block's lookup resolved ("kjv*mev.rebom" → KJV), else LDS
1215
static textVersion(result) {
1316
const v = String(result.config?.version || result.version || 'LDS');
1417
return v.split(/[.,;]/)[0].replace(/\*.*$/, '').toUpperCase() || 'LDS';
1518
}
1619

1720
async render(result, req) {
18-
const verseIds = [...new Set(result.reference?.verseIds || [])];
19-
if (!verseIds.length) return '';
20-
const version = LatexPresenter.textVersion(result);
21-
22-
const raw = await this.repo.rawVerses(verseIds, version);
21+
const blocks = result.sections?.length ? result.sections : [result];
22+
if (!blocks.some(b => b.reference?.verseIds?.length)) return '';
2323
const rows = [];
24-
for (const r of raw) {
25-
const headings = /[§▲]/u.test(r.text) ? await this.repo.loadHeading(r.verse_id, version) : null;
26-
rows.push({ verse_id: r.verse_id, format: r.format, text: r.text, headings });
24+
const seen = new Set();
25+
for (const block of blocks) {
26+
const verseIds = [...new Set(block.reference?.verseIds || [])].filter(id => !seen.has(id));
27+
if (!verseIds.length) continue;
28+
const version = LatexPresenter.textVersion(block);
29+
for (const r of await this.repo.rawVerses(verseIds, version)) {
30+
if (seen.has(r.verse_id)) continue;
31+
seen.add(r.verse_id);
32+
const headings = /[§▲]/u.test(r.text) ? await this.repo.loadHeading(r.verse_id, version) : null;
33+
rows.push({ verse_id: r.verse_id, format: r.format, text: r.text, headings });
34+
}
2735
}
2836
rows.sort((a, b) => a.verse_id - b.verse_id);
2937

‎apps/web/test/unit/latex.test.js‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ describe('helpers', () => {
148148
it('collects footnote ids from text and headings', () => {
149149
expect(footnoteIds([{ text: 'a「3」', headings: { heading: 'b「4」' } }]).sort()).toEqual([3, 4]);
150150
});
151-
it('picks the text version as PHP renderRaw did', () => {
151+
it('picks a block\'s text version: the one its lookup resolved', () => {
152152
expect(LatexPresenter.textVersion({ config: { version: 'kjv*mev.rebom' } })).toBe('KJV');
153153
expect(LatexPresenter.textVersion({ config: {} })).toBe('LDS');
154154
});
@@ -163,3 +163,25 @@ describe('formatOptions', () => {
163163
expect(option({ query: { source: 'a,b' } }, 'source')).toBe('a,b');
164164
});
165165
});
166+
167+
describe('LatexPresenter: each block in its own version (decision 2026-10-23)', () => {
168+
// PHP read the request's version (LDS) only and dropped JST verses; Node renders every block
169+
const TEXT = { LDS: { 26137: '¶For God so loved the world.' }, JST: { 68942: '¶For God so loved the world, that he gave his Only Begotten Son.' } };
170+
const repo = {
171+
rawVerses: async (ids, version) => ids.filter(id => TEXT[version]?.[id]).map(id => ({ verse_id: id, version, format: 'prose', text: TEXT[version][id], verse: 16 })),
172+
loadHeading: async () => null,
173+
footnotes: async () => new Map(),
174+
verseInfo: async ids => new Map(ids.map(id => [id, { book_id: id === 26137 ? 43 : 1043, chapter: 3, verse: 16 }])),
175+
headingLevels: async () => new Map(),
176+
};
177+
const block = (version, ids) => ({ config: { version }, reference: { verseIds: ids } });
178+
const render = result => new LatexPresenter(repo).render(result, { query: {} });
179+
it('a single JST block prints its JST verse', async () => {
180+
expect(await render(block('JST', [68942]))).toContain('Only Begotten');
181+
});
182+
it('a JST block and an LDS block print both verses', async () => {
183+
const out = await render({ ...block('JST', [68942, 26137]), sections: [block('JST', [68942]), block('LDS', [26137])] });
184+
expect(out).toContain('Only Begotten');
185+
expect(out).toContain('For God so loved the world.');
186+
});
187+
});

0 commit comments

Comments
 (0)