The browser's context budget is commonly 8 to 16 and this tree has nineteen
map-bearing profiles, so a reader who visits enough map tabs crosses it. What
the browser does then is the real problem: it kills the OLDEST context silently.
getContext never returns null, nothing fires on the new canvas, and a map that
worked a minute ago is simply blank when you go back to it. The reader who
notices is the one returning to something that used to work.
The pane now says so and offers a reload. That holds under either option on the
Driver's row 107, because it makes the failure visible rather than preventing it.
The listener goes on the canvas itself, not the pane. The map's canvas lives in
the plugin's shadow root and webglcontextlost does not bubble, so a listener on
the pane would never hear it — hence a walk that descends through shadow roots.
It binds after mount and again on every re-activation, because the canvas does
not exist when the element connects and a later one would otherwise go unwatched.
Binding is idempotent and marked per canvas.
Two string defects caught before this went anywhere. I wrote
T('actions.reloadMap'), and the string lives at buttons.reloadMap — it exists in
all eleven locales and my wrong namespace would have rendered English in every
one of them while the correct translation sat unused. And messages.mapContextLost
did not exist at all, which the tr() helper answers with a silent English
fallback. Both now resolve; keys-exist and parity are green.
|
||
|---|---|---|
| .. | ||
| 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.