Page 1 of every packet draws the zone map from mapSvg. When the map fetch fails, the page interpolated an empty string: a title, a subtitle, a privacy footnote, and nothing where the map belongs. A failed fetch and a successful render were byte-indistinguishable in the printed PDF, and the packet read as undeveloped. It is not a rare edge. The served asset pool ships boundaries, fonts, images, people-groups, pplr, previews, tables and vendor, and no posters/ directory at all, because generate-tree gates it behind posterSources and bundlePosters, both default false. Over live HTTP against the served tree: previews/sudan.webp 200, boundaries/world-adm0-web.geojson 200, posters/sudan.html 404. So on a default build every packet's page 1 has been blank. The browser path's catch around the poster fetch is right to keep printing -- a missing map must not cost the reader the people pages -- but degrading and degrading silently are different things, and only the renderer knows the map is absent when it draws the page. It now prints one quiet sentence in the map's place. The sentence is English-only on purpose. t() falls back to English per key, so the other ten locales print it in English until a translator supplies theirs, rather than ten invented translations nobody can check. The real fix for the blank map is shipping the poster sources in the asset pool; that is a tree-config change and goes separately. check-packet-map-missing-loud is two-sided: loud when the map is missing AND quiet when it is present with the SVG intact, in en/es/ar, both variants. Shown capable of failing four ways: the silent cover, a notice that always prints, a notice that displaces the map, and the raw key printed instead of a sentence. |
||
|---|---|---|
| .. | ||
| api | ||
| charts | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| elements | ||
| i18n | ||
| layouts | ||
| lists | ||
| maps | ||
| packet | ||
| packets | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| vendor/cldr | ||
| index.js | ||
| ProfileLoader.vue | ||
| README.md | ||
library/ (@map) — the shared map template
The other half of the architecture (see ../plugin-designs/README.md):
every map in plugin-designs/<your-map>/ composes the reusable, parameterized pieces that live here.
Fix something here and every map benefits on the next build; break something here and every map
breaks — read components/WARNING.md before touching shared pieces.
| Folder | What lives there |
|---|---|
ProfileLoader.vue |
The root of every bundle: parses profile-config, loads the profile, provides the stores + config |
components/ |
Shared UI — map, legends, controls, popup (PeopleGroupDetail), tables, charts, hints (README) |
composables/ |
Shared logic — map engine (useMapInstance), provider switch, shadow-DOM styles, theming |
stores/ |
Per-instance Pinia stores (ui / map / data) — provided by ProfileLoader, injected by children |
api/ |
Data-source registry (sources.json) + fetch layer — the JAMstack runtime wiring |
colors/ |
Color sets maps opt into via profile-config.colorSet |
i18n/ |
The shared string catalogs (all maps speak the same languages) |
data/ |
Small shared datasets baked at build time (big served data lives in ../data/) |
constants/ |
Shared constants |
scripts/ |
Maintainer pipelines (posters, research) — not part of any bundle |
The rule of thumb (same as WARNING.md): custom code in your app-profile, reusable code here — and things only move from the first to the second once they've earned it.