The README and documentation call the series what the product now calls it: "UPG100 List", one term, no hyphen. 28 prose mentions of "UPG-100", "UPG 100 List" and "List of 100 UPG's" change across the README (19 lines, including the print section and the layout tree, whose columns stay aligned), four documentation files and the delegation diagram's plugin label (PNG re-rendered at 2x). Paths, ids and identifiers stay as they are (my-upg-100-list, upg-100-mfe, the image file names); the one code value named, SERIES_NAME's default, follows series.json's seriesName "UPG100 List".
79 lines
4.2 KiB
Markdown
79 lines
4.2 KiB
Markdown
# Architecture — UPG100 List poster generation
|
||
|
||
> The simple overview is in `library/scripts/posters/README.md`. This is the full reference for the
|
||
> generator: files, environment, pipeline, and the gotchas that have bitten before.
|
||
|
||
The poster generator for the **my-upg-100-list** profile renders one 3ft × 3ft print poster per
|
||
zone (28 today) from the profile's own data.
|
||
|
||
**It lives inside doxa-maps on purpose.** It used to sit outside the repo in a sibling generator
|
||
folder and reach in for data via a `UPG100_DATA_PATH` env var. That cost real bugs: forget the var
|
||
and it silently rendered a **stale duplicate** of `clusters.json` — which is how Nepal, and later
|
||
Bangladesh + Bhutan, vanished from posters that looked fine otherwise. The generator and the data
|
||
it reads now ship together.
|
||
|
||
## Run it
|
||
|
||
```bash
|
||
bun run posters # all 28 posters -> out/posters/*.html
|
||
ONLY=asia-india-tamil-nadu bun run posters:one # just one (fast, ~15s)
|
||
bun run posters:shoot # HTML -> PNG @3000x3000 (Playwright/Firefox)
|
||
bun run posters:gallery # review pages -> out/index.html + out/hd.html
|
||
bun run posters:export # region folders -> ../../../upg-100-posters/
|
||
```
|
||
|
||
The full pipeline, in order:
|
||
|
||
```
|
||
posters -> posters:shoot -> (convert to WebP previews) -> bun run build
|
||
```
|
||
|
||
The WebP step feeds `plugin-designs/my-upg-100-list/assets/previews/`, which is what the poster wall
|
||
shows in the app. **A template change is not visible in the app until previews are regenerated and
|
||
the app is rebuilt.**
|
||
|
||
## Files
|
||
|
||
| File | What it does |
|
||
|---|---|
|
||
| `generate.mjs` | THE template. Reads the profile data, writes one HTML poster per zone. |
|
||
| `shoot.mjs` | Screenshots each poster to PNG (Firefox — never Chromium; privacy-first policy). |
|
||
| `poster-regions.mjs` | The region map (India, Africa, …). Shared by both gallery builders. |
|
||
| `build-gallery.mjs` | `out/index.html` — browsable preview grid, sectioned by region. |
|
||
| `build-hd-gallery.mjs` | `out/hd.html` — full-resolution review + copy-to-clipboard. |
|
||
| `export-public.mjs` | Copies PNGs into region folders OUTSIDE the repo (see below). |
|
||
|
||
## Environment
|
||
|
||
| Var | Default | Notes |
|
||
|---|---|---|
|
||
| `SPARSE=1` | — | **Required.** Selects the approved template. Without it you get the older full-vision layout. |
|
||
| `FORMAT=3x3` | `3x4` | 1500×1500 square sheet. |
|
||
| `OFFLINE=1` | — | Use the cached `out/_records.json` instead of calling the API. |
|
||
| `VITE_API_BASE_URL` | — | Satisfies the fail-closed config gate. No request is made under `OFFLINE=1`. |
|
||
| `ONLY` | — | Build a single zone by `zoneKey`. |
|
||
| `UPG100_DATA_PATH` | the profile in this repo | Only override to test against other data. |
|
||
| `SERIES_INDEX` / `SERIES_NAME` | derived / `UPG100 List` | Series line on the sheet. |
|
||
|
||
## Two things that will bite you
|
||
|
||
**1. The exported PNGs must NOT go in `public/`.** `capacitor.config.json` sets `webDir: "public"`
|
||
and Capacitor packages that directory wholesale — no per-path exclude. The export is ~52 MB, and
|
||
the per-bundle vite build also fans the served root into every MFE, so it duplicated **11×**
|
||
(631 MB) when it lived there. `export-public.mjs` therefore writes to `../../../upg-100-posters/`, a
|
||
sibling of the repo. Desktop/web can serve from there or a CDN; the phone gets posters from a CDN
|
||
or generates on device.
|
||
|
||
**2. Fail-loud guards, not silent overlaps.** `generate.mjs` throws if the header would be drawn
|
||
over the map frame, or if the below-map block would run off the sheet. Raise the constant or tighten
|
||
the copy — never delete the guard. Both have caught real regressions (`west-africa`, `sudan`,
|
||
`gujarat`, `oceania-indonesia`).
|
||
|
||
## Measurement discipline
|
||
|
||
SVG `<text>` does not word-wrap, so all multi-column setting is measured in the generator via
|
||
`CHADV -> textW -> wrapPx -> paraInto`. **That estimator is calibrated for Lato at body sizes and is
|
||
wrong elsewhere** — measured drift: Georgia bold display type runs ~1.35× wider than predicted, bold
|
||
digits ~0.98em against an assumed 0.63em, mixed bold/regular runs ~10% narrower. Every size that
|
||
must fit a box is therefore taken from the **rendered** artefact (`getBBox`), not from the formula.
|
||
When adding new display text, render it and measure before trusting a computed size.
|