On a phone, printDocument mounts the document into the page (#dxm-print-root and a print-only style) and calls window.print(). It dropped both at afterprint. But Android Chrome's print() does not block: afterprint fires once the preview is built, and the file "Save as PDF" writes is rendered later, after the reader taps Save, from the page as it is then. The preview showed a packet's 11 pages and the saved PDF was the bare host page. The document now stays until the reader is back on the page: shown again after being hidden, focused again after losing focus, or touched after print(), then BACK_GRACE_MS more for a job still rendering. A new print replaces it, a cancelled one drops it, and the ten-minute ceiling stays. Listening starts at print(), so the Print tap of a late print does not count. printImageBlob's object URL now follows the document (onRelease) instead of afterprint. On screen the document is display:none, as before. Desktop prints through the frame exactly as before. check:phone-print-keep runs the phone timeline on fake timers and a stub DOM with an Android user agent; the previous file fails it (gone after afterprint, image revoked). Its self-test poisons the old drop at afterprint, no release when back, listeners armed at mount, no grace, printImageBlob revoking at afterprint, and no ceiling. |
||
|---|---|---|
| .. | ||
| api | ||
| charts | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| dashboards | ||
| 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.