doxa-maps/plugin-designs
doxa-maps-88655c70 b9f6ecf12e
Some checks are pending
build-gate / build (push) Waiting to run
Regions is a standalone app now: URL is the state, 4/10 -> 10/10
MEASURED first, with _qa-cycle/02-builder-cycle/_probe-standalone-app.mjs: 4 of
10 app properties OK. Five of the six failures were ONE missing thing — the URL
never reflected the selection. So nothing was linkable, a reload lost the
subject, Back unmounted the element, a deep link did nothing, and Share shared
the index. DX-life gets all of it free from Nuxt routing; /regions/oceania IS
the state. A custom element has to do it deliberately.

THE HASH, NOT THE PATH: a leaf page is a static file at a fixed URL with no
server to route /regions/oceania, and the hash is the only part an EMBEDDED
element may touch without fighting its host page. Slugs are DX-life's own, so a
link reads the way theirs does. replaceState on first paint, pushState on a user
choice — that is what makes Back mean "the previous subject" rather than "leave
the page". A slug resolves region-first then country, the same order their
[slug].vue matches in; "neither" lands on the chooser rather than a 404, because
for this element the chooser is a legitimate state.

The hash is applied AFTER the fetch, not on mount: it cannot be resolved to a
subject until the regions are known, and resolving early sent every deep link
silently to the chooser.

One watcher on the selection rather than a writeUrl() call at each of the five
places a selection is made (tab-1 event, table row, two dropdowns, back-link) —
those would drift. popstate sets a guard so it cannot push an entry for a state
it just applied, which is the classic loop where Back appears to do nothing.

Also: document.title follows the subject, as theirs does via useSeoMeta, and the
scroll body is a <main> landmark — the probe measured none on the page.

★ THREE OF MY OWN ASSERTIONS WERE WRONG BEFORE THE CODE WAS, each failing on
correct behaviour, and each time my first instinct was that the app was broken:
  · h1/share/landmark counts were read AFTER a goBack had unmounted the element,
    so "0 h1s" was a reading of a blank page. A reading taken in an unknown
    state is not a finding.
  · the Back test clicked the SAME region twice — selecting the subject you are
    already on correctly creates no history entry.
  · the next version clicked two table rows, but picking a COUNTRY renders
    CountryDetail, which has no region table, so the second click hit nothing
    and both picks read identical.
It now sequences only through controls that exist in each state: pick a region,
clear with the in-page back-link, Back returns the region.

No regression: region page still 68/68 vs live, country 28/28, both browser
suites green, no gate newly red.
2026-09-24 02:20:37 +00:00
..
all-people-groups light the remaining unconditional loading overlays and the copy-me starter 2026-09-21 16:40:53 +00:00
big-picture Stop Big Picture showing the same total twice 2026-09-23 15:32:28 +00:00
cities-internet Commit the two pinHint:false metas that were only on disk 2026-09-24 00:01:08 +00:00
country-posters Search tier 2: a misspelled country resolves to THE country (best match, 2-edit cap) 2026-09-24 00:34:19 +00:00
dashboard-plus-grid Religion/Language tree TOP TIERS now filter the grid (derived-axis interop) 2026-09-23 23:00:00 +00:00
demo-map consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00
doxa-maps-spa Regions mounts its cap-4 composite; UPG-100 drops its duplicate share and shrinks its dots 2026-09-23 19:37:55 +00:00
doxa-research-map Merge main into bundler-2/work 2026-09-24 00:44:26 +00:00
doxa-simple-map Search tier 2: a misspelled country resolves to THE country (best match, 2-edit cap) 2026-09-24 00:34:19 +00:00
doxa-vision-card Brand-neutral shared VerticalTabsLayout with lazy nested-element hosting; fix cap-5 prayer tab leaf path; neutralize library identifiers; add Forgejo CI build-gate workflow 2026-09-19 17:24:30 +00:00
golden-path-check golden-path-check: walk the develop-a-plugin-and-see-it-live path end to end 2026-09-22 03:55:18 +00:00
gospel-resources Commit the two pinHint:false metas that were only on disk 2026-09-24 00:01:08 +00:00
key-cities Resolve the shared asset base per bundle, so nested tabs stop 404ing 2026-09-23 12:14:52 +00:00
my-upg-100-list Search tier 2: a misspelled country resolves to THE country (best match, 2-edit cap) 2026-09-24 00:34:19 +00:00
people-groups-grid Religion/Language tree TOP TIERS now filter the grid (derived-axis interop) 2026-09-23 23:00:00 +00:00
plugin-studio Plugin studio: show what a composite is made of, in one panel 2026-09-23 20:21:48 +00:00
prayer-walk-planner Prayer-walk POSTERS: per-zone walking-scale street map on a poster sheet 2026-09-24 01:06:51 +00:00
regions-dashboard Regions is a standalone app now: URL is the state, 4/10 -> 10/10 2026-09-24 02:20:37 +00:00
research-dashboard One prop swaps the engine for every map, not just three 2026-09-24 01:34:03 +00:00
research-map-plus-grid Map engine is one prop: MapLibre drops the search bar, legend rises 2026-09-24 00:43:44 +00:00
stack-demo Let a stacked composite find its siblings in either tree 2026-09-22 15:37:41 +00:00
template-bundle consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00
.gitkeep Rename app-profiles -> plugin-designs; build green under new naming (load-bearing paths updated, guards pass) 2026-09-17 18:18:18 +00:00
README.md consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00

plugin-designs/ — where YOUR maps live

The shared template lives in ../library/ (aliased @map) — you never edit it per-map. You build your map by making a folder here, plugin-designs/<your-map>/, and composing the template's building blocks. Your folder is yours (colors, API, i18n); the template is shared (Apache-2.0, propagated to every map on the next build).

Shared vs. yours — the balance

  library/  (@map)  ── SHARED, reusable, parameterized ──►  legend · map · map-controls
        │                                                    (every map reuses these)
        │  imported by
        ▼
  plugin-designs/<your-map>/  ── YOURS ──►  the map's "main" class (the .vue profile = the Vue
        │                                  instance for that map)
        └── src/            ── YOURS ──►  this map's OWN non-reusable components, colors, API, utils

  One bundle can hold as many maps as you want — add another .vue profile and rebuild.
  • Reusable → library/. The legend, the map, the map-controls are shared, parameterized components every map reuses. Fix once, every map benefits. Never edit it to customize one map.
  • This map's own → plugin-designs/<your-map>/. It holds two things: the map's "main" class (each .vue profile is the Vue instance for that map) and that map's own non-reusable components (in src/). Nothing here leaks to another map.
  • Many maps per bundle. The bundler bundles one map or many — add another flat .vue and rebuild.

Production or experimental — every profile declares itself

Every app profile is one of two things, and experimental profiles are unlimited and encouraged — create as many as you need:

  • Experimental, not built: prefix the folder with _ — the build skips it; it just sits in code. Zero cost, zero risk.
  • Experimental, built: builds normally, but it belongs in the Experimental section of the power app's layout (nav section) — never embedded on partner surfaces.
  • Production: a real product — main sections of the power app + individually embeddable.

(The power app = the SPA layout that composes all profiles into one application.)

The pattern in one picture

plugin-designs/<your-map>/     ← one FOLDER = one BUNDLE, named after the folder
   ├── index.js               ← entry: registers the custom element, finds the profiles
   ├── overview.vue           ← one .vue = one PROFILE (flat in the folder — no profiles/ subfolder)
   └── detail.vue             ← another profile in the SAME bundle

     builds to ─────────────► public/doxa-maps/<your-map>/<your-map>.js   (one file)
  • Folder name = bundle name. Folders starting with _ or . are skipped (handy for WIP).
  • Each .vue = one profile (one map screen). One bundle can hold many.
  • Pick the profile at embed time with the profile field — every profile shares one bundle:
<script src="/doxa-maps/<your-map>/<your-map>.js"></script>
<doxa-map profile-config='{"profile":"overview","tk":"pk.eyJ..."}'></doxa-map>
<doxa-map profile-config='{"profile":"detail","tk":"pk.eyJ..."}'></doxa-map>

Add a map — three steps

# 1. create the folder + one profile
mkdir -p plugin-designs/<your-map>
#    index.js   ← copy doxa-simple-map/index.js, rename the tag
#    <name>.vue ← your screen, built from @map/* blocks (flat in the folder)

# 2. build
npm run build            # → public/doxa-maps/<your-map>/<your-map>.js
#    (just this one:  BUNDLE=<your-map> npm run build:bundle)

# 3. embed the script tag + element (dev staging at `npm run dev` shows a paste-ready snippet)

Your three things (everything else is shared)

  • Colors — how pins are colored. Pick one the template ships (getColorStrategy('language-family')), or drop your own in plugin-designs/<your-map>/src/colors/ (start from template-bundle/src/colors/example-mode.js). Never edit @map/colors/ to recolor your map.
  • API — your map reads its data from the profile-config on the page, so the same bundle points at different data with no rebuild. DataSourceManager accepts csv (bundled file), api (one JSON URL), or rest-api (a live REST endpoint, the default). Prefer the real API over inlined JSON.
  • i18n — the template ships 11 base locales in @map/i18n/locales/. Use useI18n() for strings and RTL_LOCALES for right-to-left; add or override strings from your own profile.

The one hosting variable

The only thing that changes between hosting modes is the <script src>: a relative path when your own site serves the file, or an absolute CDN URL when a partner site serves it. The bundle is identical either way. See ../documentation/CONTRIBUTING.md for the two CDN modes and partner-site embedding.

Profile patterns (mix freely)

  • Individual — one .vue per bundle (simplest).
  • Parameterized — one profile, behavior driven by profile-config props (e.g. tabs[]).
  • Nested — one profile that renders others (workbench-style, like doxa-research-map).

See also

  • doxa-simple-map/index.js — fully commented reference bundle entry
  • template-bundle/ — minimal two-profile skeleton to copy
  • ../library/colors/README.md — the color-strategy contract
  • ../README.md — repo overview & build pipeline · ../documentation/CONTRIBUTING.md — build vs dev, CDN modes