The Driver's print standard: every export is generated when it is asked for, and only previews are
stored. The zone and country map sheets — the poster, and the map-only sheet on a packet's page 1 —
existed only if library/scripts/posters/generate.mjs (Node) had written them ahead of time; no country
had a print-resolution sheet at all.
- library/packet/poster-render.js is generate.mjs's drawing layer (2,645 lines) as a pure factory,
createPosterGenerator({ env, SERIES_DEF, ISO3_NAME, adm0, adm1Ind, adm1Chn, nePlaces, series }). Moved
VERBATIM (template literals carry their whitespace into the output), `process.env` read through a
local shadow; its inputs are its free variables, found by a scope-aware parse, not a grep (a grep
missed adm0, and the proof caught it). generate.mjs is now its Node driver.
- PROVEN BYTE-IDENTICAL: all 118 sheets of a pinned offline matrix — the 28 zones in 3x4, 3x3, letter
and packet, and a country sample at both sheet sizes — hash the same before and after.
- data/zone-sheet.js is which group pins in which zone (first claim wins), now ONE function both the
Node driver and the browser call, so a group cannot pin in one zone on paper and another on screen.
- library/packet/posterSheets.js feeds the factory in the browser: the served full-resolution
boundaries, the shared series and places tables, a generator per call (caching by format pinned the
first caller's data on every later sheet). generateCountrySheet does a country end to end;
data/zone-sheet-print.js a UPG-100 zone. ISO3_NAME moves to library/data (territory-counts re-exports).
- usePosterPrints gains opts.sheet: the poster print is the generated letter sheet as a VECTOR page,
and the packet's page 1 the generated map-only sheet (item.mapId honoured); stored images remain the
fallback when generation fails. PosterWall forwards it; the Country Posters wall, the UPG-100 wall and
the UPG-100 map pass it. The UPG-100 wall and map drop their own record fetches for the shared
provider, and the map's legend and selection HUD offer the card deck (production).
- check:poster-browser-equiv: the browser feed's sheet is byte-identical to the Node generator's for an
India zone (packet) and a country — proven two-sided (it fails when the feed drops the city dots).
check:selection-kinds reads the handler's branches instead of a fixed poster/packet pair, anchored on
`if (` (proven: it fails with the cards branch deleted). library/packet/README.md: the standard.
gates:pre 68/68; both bundles build.
85 lines
4.6 KiB
Markdown
85 lines
4.6 KiB
Markdown
# 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
|
|
|
|
```js
|
|
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 |
|