Choosing "Native" in the research map's side menu drew nothing. So did MST, and
so did Network. A control that does nothing is worse than one that is absent:
it tells the reader the map cannot do what it can.
TWO CAUSES, AND FIXING EITHER ALONE WOULD HAVE LOOKED LIKE A FIX.
1. mapStore never declared `clusteringMode`. The picker wrote to a property no
reactive system was watching, so useMapClustering's `mode` kept its own
internal default and the click changed nothing.
2. The legend's Clusters checkbox force-set 'mst' "regardless of what's in the
store", with the mode watcher "kept as a no-op for forwards compat" — its
own words. Even with (1) fixed, one tick of that checkbox would have snapped
Native back to MST: a control that works until you touch the other one.
So the two controls are now two views of one state. clusteringMode IS the state
('off' means disabled); the picker sets it; the checkbox maps onto it, promoting
only 'off' to 'mst' so the checkbox alone behaves exactly as it always has. They
can no longer show different things. The dim-slider's clusterPinOpacity got a
real home in the same pass, for the same reason it was missing.
★ THE GATE ASSERTS ON WHAT IS DRAWN, NOT ON WHAT IS SET. `clusteringMode ===
'mapbox'` would have passed the moment (1) was fixed, while the map still drew
MST lines — state was exactly the evidence that let an inert control ship. The
map now reports, per clustering layer, whether it exists, whether it is visible
and how many features are rendered; the gate clicks the real buttons and reads
that. MEASURED, before and after, on the same tree:
before off 0/0/0 native 0/0/0 mst 0/0/0 network 0/0/0
after off 0/0/0 native 37/37/0 mst 0/0/1700 network 0/0/23896
(circles/counts/lines)
It also asserts no orphan layers between modes, and that MST and Network differ
— two labels for one behaviour is the same defect as a dead option.
One measurement bug caught on the way, worth recording: a fixed 6-second pause
reported MST as drawing nothing while Network drew thousands of lines. MST over
~2,100 pins is quadratic and had simply not finished. The gate now waits for the
drawing to settle rather than guessing a duration — a gate that calls a slow
feature broken is worse than no gate.
|
||
|---|---|---|
| .. | ||
| api | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| i18n | ||
| layouts | ||
| maps | ||
| packet | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| 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.