doxa-maps/library/packet
Doxa Maps 4386b4e5bf Arabic scripture in the packet uses plain alef: U+0671 alef wasla becomes U+0627 in the stored ar strings (the blessing and Mark 5, 13 letters), display only, words and vowels unchanged
A device whose fonts lack U+0671 printed it as a box. Only those 13 code points change (0671: 13 -> 0, 0627:
+13); the bold verse is still a substring of the passage. check:packet-strings-parity fails on any U+0671 in
an ar value (it fails on the previous file). The Arabic scripture blocks also name Arabic faces before serif.
2026-10-02 20:04:41 +00:00
..
scripts check-circular-layout: use globalThis.gc, not the free identifier global 2026-10-02 19:30:09 +00:00
card-render.js Card names hyphenate only where a word cannot fit 2026-09-29 21:04:26 +00:00
circularLayout.js circularLayout: static-import forkRoute + register gate after merge 2026-10-02 15:59:38 +00:00
countrySheet.js No caller can open India's withheld poster: posterWithheld()/largeSheet() read the DEV-only preview themselves (no opener argument). The open preview is proved under Vite (check:poster-section C2, DEV true as on the pilot's dev server); check:country-sheet-one-path fails if an opener argument comes back. 2026-10-02 08:50:58 +00:00
evangelicalShare.js Each packet's % evangelical page states its own list's share 2026-09-29 02:16:31 +00:00
fork-leaders.js fork-leaders: neutral wording in the MODES comment 2026-10-02 15:50:55 +00:00
mapLabels.js Name the map's countries on page 1 in the packet's language 2026-09-29 02:16:33 +00:00
packet-render.js Arabic scripture in the packet uses plain alef: U+0671 alef wasla becomes U+0627 in the stored ar strings (the blessing and Mark 5, 13 letters), display only, words and vowels unchanged 2026-10-02 20:04:41 +00:00
packetSets.js The full packet's sets page gets its data, from every print path 2026-09-28 23:13:47 +00:00
packetStrings.js Print files: a stale cached copy of a pinned file is fetched once more, fresh 2026-10-02 19:05:37 +00:00
pins-in-state.js packet: pins-in-state — de-overlap pins while keeping each in its own state 2026-10-02 08:23:06 +00:00
poster-fetch.js Take a poster only if it is one: a host's fallback page is a miss 2026-09-29 05:26:24 +00:00
poster-raster.js Large side-lane sheets: photos print in the sheet's own document, and a raster never draws a broken image 2026-10-01 09:45:33 +00:00
poster-render.integrity.js Rebuild the packet text's pin carriers on the merged pilot: packet-strings re-pinned, its importers rebuilt, the tree and SRI regenerated 2026-10-02 19:47:03 +00:00
poster-render.js UPG-100 posters: the scripture banner sits under the title, then one three-column band like the footer's (the facts | Jesus' strategy | What is a UPG-100 List?), the text a size larger; the series is named UPG-100 List 2026-10-02 19:14:35 +00:00
posterSheets.js UPG-100 packet page 1 is the full UPG-100 poster: the letter sheet with its title, scripture banner, three-column header and footer, generated at the click 2026-10-02 19:44:34 +00:00
qr.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
README.md Map sheets are generated in the browser at the click, by the same generator the Node script runs 2026-09-29 05:26:27 +00:00
sha384.js Print files: a stale cached copy of a pinned file is fetched once more, fresh 2026-10-02 19:05:37 +00:00

The print standard — prayer resources for any set of people groups

Any map in this library can print four prayer resources for whatever its reader picked — a UPG-100 list, a country, a region, a selection on the map:

Resource What it is
Poster the set's map sheet, printed on letter paper as a vector page
Card deck one prayer card per people group, in five card stocks
Prayer list the prayer packet: full detail (map, triangle, life calendar, % evangelical, profiles, bounded + centered sets, prayer wheel, prayer points) or lightweight (map, triangle, a worksheet of every group, wheel, points)

Everything is generated at the click, in the reader's language. Nothing a reader prints is stored; only the previews on a poster wall are stored files, because a wall shows many at once.

Where it is production, where it is experimental

Production (always on): the UPG 100 list (its legend exports, selection HUD and poster wall), the Regions dashboard's country posters, and the Country Posters wall.

Experimental (one switch, library/print/experimentalExports.js): any other map whose legend already has an Export menu may offer the same exports — the card deck, the packet — only while experimentalExportsOn(profileConfig) is true. Turn it off per page (experimentalExports: false in profile-config), per visit (?exports=off), per site (window.DOXA_EXPERIMENTAL_EXPORTS = false) or everywhere (EXPERIMENTAL_EXPORTS_DEFAULT); off restores the menu the map had before. The legend shows the Card Deck item only when told to (:export-cards, or provide('legendExportCards', …) for the mobile sheet), so a map that does not handle it never shows a dead button.

Adopting it in a map — three pieces

import { usePosterPrints } from '@map/composables/usePosterPrints.js'
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
import { getApiBaseUrl } from '@map/api/apiBaseUrl.js'

// 1. RECORDS — the people of a set, by selector (one fetch per language, shared by every item)
const recordsFor = createPeopleGroupRecords({ apiBase: () => getApiBaseUrl(), lang: () => locale, profileConfig })

// 2. THE PRINTS — an item is { id, label } plus whatever your records/sheet functions need
const prints = usePosterPrints({
  records: (item) => recordsFor({ type: 'country', values: [item.iso3] }),
  lang: () => locale,
  assetsBase: () => assetsBase,
  sheet: (item, format) => mySheet(item, format),   // 3. optional: the generated map sheet
})

prints.printPacket(item, { variant: 'full' })   // or 'lightweight'
prints.printCardDeck(item)
prints.printPoster(item)

Or hand the same options to <PosterWall> as props (:records, :sheet, item-count-key, …) and the wall renders every card's Poster / Prayer list / Cards buttons itself. A wall that passes no records shows only the poster, rather than buttons that fail.

Selectors (peopleGroupRecords.js)

Selector Picks
{ type: 'country', values: ['TCD'] } the groups of those countries (ISO3)
{ type: 'pg_slugs', values: [...] } exactly those groups — a selection, a custom list
{ type: 'field', field: 'wagf_region', values: ['africa'] } any field's value
anything else your evaluate(selector, records) (the UPG-100 selector language)

An unknown selector selects nothing, never everything.

The map sheet (posterSheets.js)

generateSheet({ zone, pins, zoneFC, format, seriesId, ISO3_NAME, adm0, adm1Ind, adm1Chn, series }) draws a sheet with the same generator the Node script runs (poster-render.js). Formats: 3x4, 3x3 (wall posters), letter (the poster print), packet (the map-only sheet for a packet's page 1). loadBoundaries(assetsBase) fetches the full-resolution boundaries it draws from. generateCountrySheet({ country, records, assetsBase, ISO3_NAME, format, series }) does all of it for one country. A UPG-100 zone: plugin-designs/my-upg-100-list/data/zone-sheet-print.js.

Page 1 of a packet takes the first of: an explicit mapSvg, sheet(item, 'packet'), mapImage(item) (a raster of a live map), a stored sheet under assetsBase. With none, it says the map is missing.

What proves it

Gate Asserts
check:people-group-records a selector picks exactly its set, two-sided
check:poster-browser-equiv a sheet generated in the browser is byte-identical to the Node generator's
check:packet-a1-structure, check:packet-variants both packets' page order and contents
check:map-labels, check:plural-forms, check:i18n-* every word in the reader's language