Packet print at reading size: the prayer points flow over two sheets at ~9pt, and Full detail's tally heads take two lines over the worksheet's 160px columns
Points: text 22.5px (~9.2pt on letter), headings 25px, the Mark 5 passage 20px, the themes 21px; the themes and points 1–6 on the first sheet, 7–12 and the credit on the second, each titled with its range. Measured in all 11 languages: the fullest first sheet keeps ~330px. Full detail: tally columns 160px (the worksheet's), heads 15px in two lines in every language, the first-page block 720px (the tallest, ru, uses 692), 11 rows on the first page. Gates: the render gate measures BOTH points sheets (pointsOver), counts the points by .pt-h across the document, expects 2 points sheets; the structure gate reads consecutive points sheets as one entry; profile-table probes the 15px tier between the two budgets wherever they fall.
This commit is contained in:
parent
2e0422f6fa
commit
4d4fa4f6d0
4 changed files with 34 additions and 21 deletions
|
|
@ -78,7 +78,7 @@ function sequence(html) {
|
|||
: /word-title/.test(seg) ? 'word' // point 4's passage in full + the Father's-heart list (2026-09-30)
|
||||
: /points-title|points_title|Prayer Points/.test(seg) ? 'points'
|
||||
: `?${cls.trim()}`
|
||||
if ((kind === 'profiles' || kind === 'worksheet') && out[out.length - 1] === kind) return // one entry per run
|
||||
if ((kind === 'profiles' || kind === 'worksheet' || kind === 'points') && out[out.length - 1] === kind) return // one entry per run
|
||||
out.push(kind)
|
||||
})
|
||||
return out.join(' · ')
|
||||
|
|
|
|||
|
|
@ -279,8 +279,11 @@ const r = await page.evaluate(() => {
|
|||
return Math.round(Math.max(0, ...[...b.children].map((k) => k.getBoundingClientRect().bottom + parseFloat(getComputedStyle(k).marginBottom))) - top - b.clientHeight) })(),
|
||||
// The points page is ONE sheet by design and fits only because its sizes were tuned to the longest locale;
|
||||
// measure its content against its box (studio's ask), as fdTopOver does for the list's first page.
|
||||
pointsOver: (() => { const b = document.querySelector('.points-body'); if (!b) return null; const top = b.getBoundingClientRect().top
|
||||
return Math.round(Math.max(b.scrollHeight - b.clientHeight, ...[...b.children].map((k) => k.getBoundingClientRect().bottom - top - b.clientHeight))) })(),
|
||||
// The points flow over TWO sheets (2026-10-02); each sheet's body is measured, the worst one reported.
|
||||
pointsOver: (() => { const bs = [...document.querySelectorAll('.points-body')]; if (!bs.length) return null
|
||||
return Math.max(...bs.map((b) => { const top = b.getBoundingClientRect().top
|
||||
return Math.round(Math.max(b.scrollHeight - b.clientHeight, ...[...b.children].map((k) => k.getBoundingClientRect().bottom - top - b.clientHeight))) })) })(),
|
||||
pointsSheets: document.querySelectorAll('.sheet-points').length,
|
||||
funnelTip: document.querySelectorAll('.funnel-tip').length,
|
||||
lifeShaded: (() => { const g = document.querySelector('.sheet-christian .lc-svg'); return g ? window.__lcBoxes(g).shaded.length : null })(),
|
||||
// ★ FILLED FROM THE TOP, in reading order (the packet's design), measured on the DOM order of the cells:
|
||||
|
|
@ -456,7 +459,7 @@ const r = await page.evaluate(() => {
|
|||
if (!host) return -1
|
||||
// Each point's heading carries class="pt-h": a numbered LINE is not a point. German cites "1. Petrus 5:6–11"
|
||||
// (its own name for 1 Peter), and that reference line was counted as a thirteenth point.
|
||||
const heads = host.querySelectorAll('.pt-h')
|
||||
const heads = document.querySelectorAll('.pt-h')
|
||||
if (heads.length) return heads.length
|
||||
return (host.innerText || '').split('\n').filter((l) => /^\s*\d{1,2}\.\s/.test(l)).length
|
||||
})(),
|
||||
|
|
@ -783,7 +786,9 @@ if (r.fdTopOver > 0) fails.push(`the Full detail's explanation block outgrows it
|
|||
if (r.wsRows && r.wsSheets !== wsPages(r.wsRows)) fails.push(`${r.wsRows} worksheet rows should take ${wsPages(r.wsRows)} page(s) but the packet has ${r.wsSheets}`)
|
||||
// + 2 again (2026-09-30, the second revision): point 4's Mark 5:18-20 and the Father's-heart list are back ON the
|
||||
// points page, so the back matter is the wheel and the points.
|
||||
const expectedSheets = 1 + lifeSheets + funnelSheets + listSheets + setsSheets + 2
|
||||
// + 3 (2026-10-02): the wheel, and the points on TWO sheets (themes + points 1–6, then 7–12), read at print size.
|
||||
if (r.pointsSheets !== 2) fails.push(`the prayer points should flow over 2 sheets, found ${r.pointsSheets}`)
|
||||
const expectedSheets = 1 + lifeSheets + funnelSheets + listSheets + setsSheets + 3
|
||||
// ── THE LIFE-CALENDAR PAGES, asserted rather than merely counted ──────────────────────────────────
|
||||
// A page that EXISTS is not a page that is RIGHT: an empty grid, a grid of the wrong size, or a
|
||||
// christian page whose shading silently fell to zero would all still be one sheet. 5,200 is the
|
||||
|
|
|
|||
|
|
@ -121,7 +121,9 @@ const probe = (txt) => {
|
|||
const words = (n) => 'word '.repeat(n).trim()
|
||||
ok(probe(words(10)).px === 17, 'a short description did not render at the full 17px')
|
||||
ok(probe(words(Math.floor(budget(17) / 5))).px === 17, 'a description just inside the 17px budget stepped down anyway')
|
||||
ok(probe(words(Math.floor(budget(17) / 5) + 6)).px === 15,
|
||||
// Between the 17px and the 15px budgets, wherever they fall for this row width: '+6 words' was 30 characters, and
|
||||
// when the tally cells widened the gap between the two budgets shrank to 24, so the probe skipped 15 for 14.
|
||||
ok(probe(words(Math.floor((budget(17) + budget(15)) / 2 / 5))).px === 15,
|
||||
'a description over the 17px budget did not step to 15px')
|
||||
const long = probe(words(Math.floor(budget(14) / 5) + 40))
|
||||
ok(long.px === 14, `a very long description did not clamp at the smallest size (got ${long.px}px)`)
|
||||
|
|
|
|||
|
|
@ -131,13 +131,13 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
// The tally cells no longer carry their question under each box: the three questions head the table ONCE
|
||||
// per page (.pray-head), so a cell is all tally room. 84 -> 116 gives every box more of it and the heads a
|
||||
// readable 15px (three short lines; at 96px they were three tiny ones).
|
||||
const PRAY_CELL = 116;
|
||||
const PRAY_CELL = 160;
|
||||
// The question heads above the first row of every page, and the first page's explanation (the intro,
|
||||
// the blessing, the band) in a reserved block, so the first page holds FD_FIRST rows and the rest 20.
|
||||
// FD_TOP_H is MEASURED: with a 100-group list the tallest locales' block uses 612px (es fr pt de it ro ru hi;
|
||||
// en 591, ar 507, zh 513), so 640 keeps 28px; with the 62px head row the first page holds 11 rows. check:packet-render fails a block
|
||||
// whose content outgrows it (the block clips rather than overlap the rows, so only a measurement sees it).
|
||||
const PRAY_HEAD_H = 62, FD_TOP_H = 640;
|
||||
const PRAY_HEAD_H = 62, FD_TOP_H = 720;
|
||||
const FD_ROWS_TOP = M + HEAD + FD_TOP_H + PRAY_HEAD_H;
|
||||
const SET_PANEL = 660; // one square sets panel: two + a 40px gap fit 1380px
|
||||
const SET_SOLO = 960; // one chart per page: a large square, centred
|
||||
|
|
@ -374,7 +374,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
/* The three questions, ONCE per page, over the tally cells' own columns. */
|
||||
.pray-head{position:absolute;inset-inline-end:${M}px;display:grid;grid-template-columns:repeat(${PRAY_COLS.length},${PRAY_CELL}px);
|
||||
height:${PRAY_HEAD_H - 6}px;align-items:end;font:700 15px/1.2 Georgia,serif;color:#6f6a5c;text-align:center}
|
||||
.pray-head span{text-wrap:balance;padding:0 3px 5px;border-bottom:2px solid #3b463d}
|
||||
.pray-head span{text-wrap:balance;padding:0 0 5px;border-bottom:2px solid #3b463d}
|
||||
.fd-top{position:absolute;left:${M}px;top:${M + HEAD}px;width:${CW.toFixed(0)}px;height:${FD_TOP_H}px;overflow:hidden}
|
||||
.hd-r{position:absolute;right:${M}px;top:54px;font:400 19px Georgia,serif;color:#8c8170}
|
||||
/* ⚠ box-sizing:border-box IS LOAD-BEARING HERE, NOT HOUSEKEEPING. Without it the row's height
|
||||
|
|
@ -1352,11 +1352,13 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
// Mark 5:18-20 and the Father's-heart list share this ONE sheet, in every locale: the sizes below are the
|
||||
// largest that fit the longest locale. class="points-body" is a TEST HOOK: the page must fit ONE sheet in
|
||||
// every locale (its scrollHeight within its clientHeight).
|
||||
// ★ 2026-10-02: the prayer themes now head the page at a size that reads on paper (21px, ~8.6pt on letter) and
|
||||
// the credit line sits 0.28in above the sheet's edge, so the points give back a little: 20 → 18px text, 10 → 5px
|
||||
// between points, the Mark 5 passage 18 → 16px, and the body starts 14px higher (the heading stays 22). Measured
|
||||
// in all 11 locales: the tightest (fr, hi) keep 46px under the last point; check:packet-render fails overflow.
|
||||
const PT = { head: 22, sub: 19, text: 18, refs: 16, gap: 5, passage: 16 };
|
||||
// ★ READABLE ON PAPER, ON TWO SHEETS (2026-10-02): the point text at 22.5px (~9.2pt on letter), the themes at
|
||||
// 21px (~8.6pt), the Mark 5 passage at 20px; that no longer fits one sheet, so the points FLOW onto a second: the
|
||||
// themes and points 1–6 (point 4 carries its passage and the Father's-heart list) on the first, 7–12 on the
|
||||
// second, which carries the credit line. Measured in all 11 locales: the fullest first sheet (fr, de) keeps
|
||||
// ~350px; check:packet-render fails either sheet's overflow (pointsOver).
|
||||
const PT = { head: 25, sub: 21, text: 22.5, refs: 18.5, gap: 18, passage: 20 };
|
||||
const POINTS_FIRST = 6;
|
||||
// ⚠ ISOLATE EACH REFERENCE FOR BIDI. Scripture references stay in English
|
||||
// (a reader looks them up in their own Bible), so on an RTL page they are
|
||||
// LTR runs inside an RTL paragraph. Without isolation the Unicode bidi
|
||||
|
|
@ -1405,14 +1407,18 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
<div style="font:600 ${PT.refs}px Georgia,serif;color:#7a3b2e;margin-top:2px">${refs}</div>
|
||||
${p.print_passage ? passageBlock() + heartBlock() : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
return sheet(`
|
||||
});
|
||||
// Each sheet names its range (1–5, 6–12) beside the title, in the language's digits.
|
||||
const head = (from, to, intro) => `
|
||||
<div style="position:absolute;left:${M}px;top:44px;right:${M}px">
|
||||
<div class="points-title" style="font:700 40px Georgia,serif;color:#1b1a17">${esc(t('points_title'))}</div>
|
||||
<div style="font:italic 18px Georgia,serif;color:#6d766e;margin-top:6px">${esc(t('points_intro'))}</div>
|
||||
</div>
|
||||
<div class="points-body" style="position:absolute;left:${M}px;top:${M + 82}px;right:${M}px;bottom:80px">${themesBlock()}${rows}</div>
|
||||
<div class="fine">${esc(t('points_attrib'))}</div>`);
|
||||
<div class="points-title" style="font:700 40px Georgia,serif;color:#1b1a17">${esc(t('points_title'))}
|
||||
<span class="pt-range" style="font:400 24px Georgia,serif;color:#8c8170">${num(from)}–${num(to)}</span></div>
|
||||
${intro ? `<div style="font:italic 18px Georgia,serif;color:#6d766e;margin-top:6px">${esc(t('points_intro'))}</div>` : ''}
|
||||
</div>`;
|
||||
const body = (html, top) => `<div class="points-body" style="position:absolute;left:${M}px;top:${top}px;right:${M}px;bottom:80px">${html}</div>`;
|
||||
return sheet(`${head(1, POINTS_FIRST, true)}${body(themesBlock() + rows.slice(0, POINTS_FIRST).join(''), M + 96)}`, 'sheet-points')
|
||||
+ '\n' + sheet(`${head(POINTS_FIRST + 1, POINTS.length, false)}${body(rows.slice(POINTS_FIRST).join(''), M + 70)}
|
||||
<div class="fine">${esc(t('points_attrib'))}</div>`, 'sheet-points');
|
||||
}
|
||||
|
||||
// ★ THE HEAVENLY FATHER'S HEART (2026-09-30): a plain statement of what they need to know (point4_heart_lede,
|
||||
|
|
|
|||
Loading…
Reference in a new issue