The footer's attribution is a licence statement, so it has to be true rather than merely present. posterDefaults ships "(c) Mapbox (c) OpenStreetMap" and any caller that did not override it printed exactly that -- including country-posters, whose maps run on the fleet's MapLibre default, where Mapbox serves nothing at all and OpenFreeMap, OpenMapTiles and OpenStreetMap are the three parties whose licences actually apply. research-map had a hand-written pair for the two providers, which was right for research-map and helped nobody else. The brief said to read it from the style's sources. Checked against the live style rather than assumed, and it does not work: tiles.openfreemap.org/styles/liberty declares two sources and NEITHER carries an attribution field. The credit lives in the TileJSON the vector source points at, which the engine fetches and merges into the SOURCE OBJECT once it loads. So the style is the request and the source is what came back, and only the second one knows. map-attribution.js reads the live source first and falls back to any style-declared value. The TileJSON value is HTML with anchors and a © entity, and a poster footer is text, so it is flattened rather than printed raw. Repeated credits collapse. When nothing can be read -- no map, a style that throws before it has loaded -- it returns empty and the caller keeps its own string, because a blank licence line is the one outcome that is never acceptable. The check uses the real TileJSON string as its fixture. A tidy invented style would have passed while the real map printed the wrong credit, and its first negative test is exactly that: reading the style alone returns nothing for OpenFreeMap. |
||
|---|---|---|
| .. | ||
| 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.