tauri build produced target/release/doxa-maps-desktop (65 MB ELF) and a 58 MB .deb (Package: doxa-maps, Depends: libwebkit2gtk-4.1-0 libgtk-3-0). tauri info reports the environment fully green and correctly detects frontendDist ../public, Vue.js, Vite. The web tree is compiled INTO the binary rather than shipped as files, which is why the .deb holds only 6 entries - confirmed by 19 distinct embedded doxa-maps/... asset paths in the ELF and by size (a logic-free shell is 65 MB only because the 132 MB public/ tree is compressed inside it). bundle.targets narrowed from all to deb: rpmbuild and linuxdeploy are absent here, so all would fail two of three formats at the END of a ~6-minute release build. The CLI is a devDependency (@tauri-apps/cli, prebuilt binary) rather than cargo install tauri-cli, which compiles from source. Status stays BUILT, NOT SHOT. The GUI launch is unverified because this box is headless - the binary panics in tao with 'Failed to initialize GTK', the absence of a display rather than a defect, and no Xvfb is installed.
308 lines
18 KiB
Markdown
308 lines
18 KiB
Markdown
# doxa-maps — one build, maps that embed everywhere
|
|
|
|
**A Vite bundler that turns each map into a self-contained web component.** One `bun run build` produces a
|
|
`doxa-maps/` folder you drop on any host — iframe it, script-embed it, ship it as a mobile app, or open the
|
|
whole thing as one SPA. **Same code, every screen.**
|
|
|
|
This README is a **show, don't tell** index. Skim the three tables, then jump to the section you need.
|
|
|
|
---
|
|
|
|
## Index
|
|
|
|
### The 5 plugin (app) types — from simplest to full app
|
|
|
|
| # | Capability | What it is | Example plugin | Section |
|
|
|---|------------|------------|----------------|---------|
|
|
| 1 | **Single element** | One folder → one bundle → one custom element. The lightest tier. | `doxa-vision-card` | [§1](#cap-1--single-element) |
|
|
| 2 | **Preset element (prop-table)** | ONE `root.vue` rendered N ways by preset props — one plugin, many maps. | `doxa-simple-map` (prayer · engagement · adoption) | [§1](#cap-2--preset-element-prop-table) |
|
|
| 3 | **Monolithic / multi-view** | A whole research map, all in one `root.vue`. | `doxa-research-map` | [§1](#cap-3--monolithic-research-map) |
|
|
| 4 | **Composite element** (nested-element SPA) | A composite element that nests other elements and wires them via props/events. | `my-upg-100-list` | [§1](#cap-4--composite-element) |
|
|
| 5 | **App shell** (nested-SPA) | The full doxa-maps app — every plugin in one deep-linkable page. | the doxa-maps SPA | [§1](#cap-5--app-shell--the-doxa-maps-app) |
|
|
|
|
### The 5 deployment methods
|
|
|
|
| # | Method | Status | Section |
|
|
|---|--------|--------|---------|
|
|
| 1 | **iframe embed** on any site | required | [§2](#deploy-1--iframe-embed) |
|
|
| 2 | **Mobile app** (Android / Capacitor) | required | [§2](#deploy-2--mobile-app) |
|
|
| 3 | **Direct-JS / research dashboard** (props + events + SRI) | required | [§2](#deploy-3--research-dashboard-direct-js) |
|
|
| 4 | **WordPress** (PHP plugin: shortcode + Gutenberg block) | built, not yet shot | [§3](#3-cli-commands--installs-per-deploy-method) |
|
|
| 5 | **Desktop** (Tauri) | built, not shot (binary + `.deb` emitted; launch unverified) | [§3](#3-cli-commands--installs-per-deploy-method) |
|
|
|
|
### CLI commands + what to install
|
|
|
|
Install **[Bun](https://bun.sh)** once, then:
|
|
|
|
```bash
|
|
bun install # download node_modules (one time)
|
|
bun run dev # live plugin gallery at http://localhost:5173
|
|
bun run build # build every plugin-designs/<map>/ → public/doxa-maps/
|
|
bun run serve # preview the built tree
|
|
bun run phone # build → Capacitor → Android APK on a connected device
|
|
```
|
|
|
|
The full command matrix (per deploy method, with prereqs like the Android SDK / JDK for mobile) lives in
|
|
**§3 → [docs/README-SECTION-3-cli-commands.md](./docs/README-SECTION-3-cli-commands.md)**.
|
|
|
|
> **The data caveat:** maps ship **without data**. Being JAMstack, each fetches its people-group data from an
|
|
> **API** at runtime, and its Mapbox token from a token endpoint (never baked into the bundle). That is what
|
|
> keeps them embeddable everywhere. See [docs/how-to.md](./docs/how-to.md).
|
|
|
|
---
|
|
|
|
## 1. Plugin types — one screenshot each
|
|
|
|
Every image below is a real map from this repo's `bun run build` output (`public/doxa-maps/`), captured in a
|
|
headless browser.
|
|
|
|
### CAP 1 — Single element
|
|
|
|
*One folder → one bundle → one custom element.* The lightest tier: `plugin-designs/all-people-groups/` —
|
|
one `.vue`, no preset table, one embeddable element showing every people group on one map.
|
|
|
|
> **What makes it cap-1 rather than cap-2:** a bundle with a `<profile>.instances.json` is typed
|
|
> `parameterized` (cap-2) by `discover.mjs`, because one profile then renders as *many* preset pages.
|
|
> `all-people-groups` ships no instances file, so `manifest.json` types it `single`. That file — not the
|
|
> folder's apparent simplicity — is what decides the tier.
|
|
|
|

|
|
|
|
### CAP 2 — Preset element (prop-table)
|
|
|
|
*One `root.vue`, many maps.* `plugin-designs/doxa-simple-map/` is a single parameterized profile. A preset
|
|
prop-table (`simple-map.instances.json`) renders it three ways — **prayer**, **engagement**, **adoption** —
|
|
each a standalone embeddable page. Same code, three maps.
|
|
|
|
| Prayer | Engagement | Adoption |
|
|
|--------|------------|----------|
|
|
|  |  |  |
|
|
|
|
*All three come from the **same** `doxa-simple-map` plugin-design folder — differentiated only by preset props.*
|
|
|
|
### CAP 3 — Monolithic research map
|
|
|
|
*A whole research map, all from one `root.vue`.* `plugin-designs/doxa-research-map/` — the monolithic tier.
|
|
|
|

|
|
|
|
### CAP 4 — Composite element
|
|
|
|
*A composite element that nests lower-tier elements and wires them via props/events.* `plugin-designs/my-upg-100-list/` —
|
|
the UPG-100 list plugin. It ships **three profiles**, each its own embeddable leaf: the composite
|
|
(`upg-100-mfe`), the list (`upg-100-list`), and the poster wall (`poster-wall`).
|
|
|
|
| Composite element (`upg-100-mfe`) | List (`upg-100-list`) | Poster wall (`poster-wall`) |
|
|
|-----------------------------------|-----------------------|-----------------------------|
|
|
|  |  |  |
|
|
|
|
### CAP 5 — App shell / the doxa-maps app
|
|
|
|
*The top tier: every plugin in one deep-linkable single-page app* — `public/index.html`, generated by the
|
|
build. A vertical-tab sidebar groups every plugin by capability (CAP 5 → 1); clicking a row mounts that
|
|
plugin. This same SPA is the Capacitor mobile-app entry (see deploy 2).
|
|
|
|
**`plugin-studio` is itself a CAP-5 plugin, AUTO-GENERATED by the Vite build step.** You do not hand-write
|
|
it — the build discovers every folder in `plugin-designs/`, bundles each, and generates the studio SPA that
|
|
composes them (a `nested-spa-single-spa` / app-shell tier plugin). Add a plugin-design folder, run the
|
|
build, and it appears in the studio automatically. (`doxa-maps-spa` is the curated product SPA; `plugin-studio`
|
|
is the auto-generated gallery of everything built — same cap-5 tier, both built by `bun run build`.)
|
|
|
|
**Two shared layouts back the cap-5 tier** (in `library/layouts/`, brand-neutral + reusable by any plugin):
|
|
- **`VerticalTabsLayout`** — one element visible at a time (the sidebar-tab shell above). Lazy-mounts each
|
|
tab's element on first open.
|
|
- **`StackLayout`** — the *opposite*: all elements STACKED top-to-bottom at natural height, so the host page
|
|
scrolls through them (the doxa-life / nuxtinator page-flow model). Group two elements and embed the pair.
|
|
|
|
**The two cap-5 apps, side by side.** They are different products at the same tier and the distinction
|
|
matters when you embed one: `doxa-maps-spa` is what an END USER gets, `plugin-studio` is what a DEVELOPER
|
|
reviews. (Captioning a studio shot as the product SPA is a documented mistake — see `/plugin-designer`.)
|
|
|
|
*`doxa-maps-spa` — the curated PRODUCT SPA.* Vertical tabs grouped by audience, not by capability:
|
|
Prayer Mobilization (Big Picture · UPG-100 List · Prayer Progress · Gospel Resources) → Research
|
|
(Cities & Internet · Research Map) → Experimental. Tab 1 is the cap-4 Big Picture composite.
|
|
|
|

|
|
|
|
*`plugin-studio` — the AUTO-GENERATED developer gallery.* The same cap-5 tier, but the sidebar groups every
|
|
built plugin by capability (CAP 5 → 1) so an author can review the whole catalogue after a build.
|
|
|
|

|
|
|
|
---
|
|
|
|
## 2. Deployment methods — one screenshot each
|
|
|
|
The exact commands + install prereqs for each are in **§3**. These shots show what each method looks like live.
|
|
|
|
### Deploy 1 — iframe embed
|
|
|
|
*The simplest.* One `<iframe>` line drops a map into an "embed section" on any partner site — no build step on
|
|
their site. Below: the prayer map running in an iframe inside a church's missions page.
|
|
|
|

|
|
|
|
### Deploy 2 — mobile app
|
|
|
|
*Same code, native app.* `bun run phone` wraps the SPA into an Android app via Capacitor. Below: the doxa-maps
|
|
SPA at a phone viewport — the exact web output that `cap:sync` copies into the APK.
|
|
|
|
<p align="center">
|
|
<img src="docs/readme-images/deploy2-mobile-spa.png" alt="Deploy 2 — the doxa-maps SPA at phone viewport (the Capacitor mobile-app surface)" width="280">
|
|
</p>
|
|
|
|
### Deploy 3 — research dashboard (direct-JS)
|
|
|
|
*Props + events + SRI.* `plugin-designs/research-dashboard/` embeds via a direct `<script src>` (with a
|
|
Subresource-Integrity hash) so the map talks to the surrounding page — a coordinated-views dashboard where the
|
|
map, the facet rail, and the people-groups table brush-and-link over one store.
|
|
|
|

|
|
|
|
---
|
|
|
|
## 3. CLI commands + installs (per deploy method)
|
|
|
|
The complete, copy-paste command matrix — every deploy method mapped to its install prereqs and exact
|
|
commands, grounded in this branch's `package.json` — lives in its own doc:
|
|
|
|
**→ [docs/README-SECTION-3-cli-commands.md](./docs/README-SECTION-3-cli-commands.md)**
|
|
|
|
Quick reference:
|
|
|
|
| # | Deploy method | Install first | Command(s) |
|
|
|---|---------------|---------------|------------|
|
|
| 1 | **iframe embed** | Bun | `bun install` → `bun run build` → `bun run serve` (or drop `public/doxa-maps/` on any host) |
|
|
| 2 | **Mobile (Android)** | Bun alone for `check:mobile`; Android SDK + `adb` + JDK 17 for the APK | `bun run check:mobile` (contract check, no toolchain) → `bun run phone` (full APK path) |
|
|
| 3 | **Direct-JS / dashboard** | Bun | `bun run build`, then paste the `<script src> + integrity` snippet from `manifest.json`. *Proven same-root AND on a foreign host page: the snippet now carries `assetsBase` + the `MAP_APP_API_URL` global; fill the `YOUR_*` placeholders* |
|
|
| 4 | **WordPress** | Bun | `bun run build`, then install the `wordpress/doxa-maps-embed/` PHP plugin (shortcode + Gutenberg block) *(built + contract-proven via `bun run check:wp-embed`; not yet shot on a live WP install)* |
|
|
| 5 | **Desktop (Tauri)** | Rust toolchain + Tauri CLI | `bun run check:tauri` (contract check, no toolchain needed) · `cargo tauri build` for the binary *(built, not shot — no binary emitted here)* |
|
|
|
|
Every build writes `public/doxa-maps/manifest.json` — a machine-readable index carrying the ready-to-paste
|
|
**iframe** and **`<script src>` + SRI** embed snippet for every map.
|
|
|
|
---
|
|
|
|
## 4. Repository layout (hand-off map)
|
|
|
|
Two directories matter to a plugin author: **`library/`** (the shared, high-bar reusable toolkit) and
|
|
**`plugin-designs/`** (your free zone — one folder per plugin). The build reads `plugin-designs/`, pulls
|
|
what it needs from `library/`, and emits each plugin to **`public/doxa-maps/<bundle>/<bundle>.js`**
|
|
plus a standalone page per profile.
|
|
|
|
> **That is the whole contract: write your app inside `plugin-designs/<your-map>/` and `bun run build`
|
|
> puts it in the output tree — no per-plugin wiring, no registration step.** Drop a new folder in and
|
|
> the next build picks it up.
|
|
>
|
|
> The `doxa-maps` segment is the **output tree name**, set by `name` in
|
|
> `vite.doxa-maps-build-config.json` — it is not your plugin's name and not this repo's folder name.
|
|
> (This line previously read `public/<name>/`, which conflated the two and sent a reader looking in a
|
|
> directory that does not exist.)
|
|
|
|
### `library/` — the shared reusable toolkit
|
|
|
|
Imported by ANY plugin. High blast radius — see [Library governance](#5-library-governance) before editing.
|
|
|
|
```
|
|
library/
|
|
├── index.js ← the library's public surface (what plugins import)
|
|
├── ProfileLoader.vue ← the runtime loader that mounts a plugin profile
|
|
├── layouts/ ← reusable shell layouts (VerticalTabsLayout, app-wrapper-material, pane-wrapper)
|
|
├── components/ ← reusable Vue components (map controls, search, tables, charts, legends, detail panels)
|
|
├── composables/ ← reusable logic hooks (useMapData, useMapClustering, useGeocoderSearch, legends/, poster/)
|
|
├── colors/ ← brand-neutral color scales + registry (prayer-progress, adoption, engagement, religion, language-family)
|
|
├── constants/ ← shared defaults (map defaults, zoom, poster sizes)
|
|
├── stores/ ← shared Pinia-style stores (dataStore, mapStore, uiStore)
|
|
├── i18n/ ← shared translations (index + locales/)
|
|
├── api/ ← data-source layer (DataSourceManager, source registry, sources.json)
|
|
├── data/ ← shared static reference data (ISO tables, language-family lookups)
|
|
├── utils/ ← shared helpers (clustering, MST/network utils, data-source helpers)
|
|
├── styles/ ← shared style registry
|
|
└── scripts/ ← shared build/gen scripts (posters, research)
|
|
```
|
|
|
|
### `plugin-designs/` — your free zone (one folder per plugin)
|
|
|
|
You write your app HERE. Each folder is its own application; on `bun run build` it compiles to
|
|
`public/doxa-maps/<bundle>/<bundle>.js` and becomes its own deployable custom element (via
|
|
`defineCustomElement`).
|
|
|
|
```
|
|
plugin-designs/
|
|
├── template-bundle/ ← copy this to start a new plugin
|
|
├── doxa-vision-card/ ← CAP 1: single element (one .vue + index.js)
|
|
│ ├── doxa-vision-card.vue
|
|
│ └── index.js
|
|
├── doxa-simple-map/ ← CAP 2: preset element (one root.vue + *.instances.json → many maps)
|
|
├── doxa-research-map/ ← CAP 3: monolithic multi-view map
|
|
├── my-upg-100-list/ ← CAP 4: composite element (nests lower-tier elements)
|
|
├── doxa-maps-spa/ ← CAP 5: app-shell (the full doxa-maps app, one deep-linkable SPA)
|
|
│ ├── doxa-maps-spa.vue
|
|
│ ├── doxa-maps-spa.meta.json
|
|
│ ├── doxa-maps-spa.instances.json
|
|
│ └── index.js
|
|
├── research-dashboard/ ← direct-JS dashboard (props/events/SRI; deploy method 3)
|
|
├── big-picture/ country-posters/ key-cities/ cities-internet/ … ← more example plugins
|
|
```
|
|
|
|
**How a plugin-design works:** create a folder in `plugin-designs/<name>/` (copy `template-bundle/`),
|
|
write your `.vue` (plus an optional `<name>.meta.json` to declare its type and `<name>.instances.json` for
|
|
preset rows), and run `bun run build`. The build auto-detects the folder, compiles it into a self-contained
|
|
element at `public/doxa-maps/<bundle>/`, and adds its embed snippets to `public/doxa-maps/manifest.json`. Your plugin
|
|
imports shared pieces from `library/` but ships as its own deployable artifact.
|
|
|
|
### Deploy honesty ledger
|
|
|
|
This README does not over-claim. Per the verified ledger (2026-09-19):
|
|
|
|
| Count | State | Methods |
|
|
|-------|-------|---------|
|
|
| 2 | **proven live** | iframe embed · direct-JS embed |
|
|
| 3 | **built, not yet shot** | mobile (Capacitor scaffolded, APK never emitted) · WordPress (`wordpress/doxa-maps-embed/*.php` written, install never captured) · desktop (`src-tauri/`; **65 MB binary + 58 MB .deb emitted** — launch unverified, headless box) |
|
|
| 0 | **aspirational** | — none. Method 5 moved to built-not-shot on 2026-09-21. |
|
|
|
|
All **5 capabilities** are proven by a built bundle. Every deploy *method* is now at least scaffolded; three are
|
|
not yet shot (no APK, no live WP install, no desktop binary).
|
|
|
|
---
|
|
|
|
## 5. Library governance
|
|
|
|
The two zones above have very different rules. Follow them so one author's change never breaks another's map.
|
|
|
|
**`plugin-designs/` = free zone.** Agentically engineer your maps here without asking. A plugin operates as
|
|
its own application, isolated to its own folder, and builds to its own output — nothing you do here can break
|
|
another author's plugin.
|
|
|
|
**`library/` = HIGH BAR.** It is multi-tenant: reused by many maps and authors. Editing it is a
|
|
breaking-change risk to everyone. From the canonical anti-pattern
|
|
(`AP-fill-shared-library-with-non-reusable-components`):
|
|
|
|
> Agentically engineer your maps IN plugin-designs — a plugin operates as its own application that builds to
|
|
> the output folder on the build step. Only contribute to the shared library if you're experienced AND the
|
|
> component is genuinely reusable + parameterized so OTHER maps (not just yours) can use it. Treat every
|
|
> library edit as breaking-change-risk to other tenants — a HIGH BAR. Never widen or casually add.
|
|
|
|
Why it matters: a non-reusable component in `library/` teaches other agents + future authors that it's OK to
|
|
follow the anti-pattern, polluting a high-blast-radius surface over time — and any library edit can make a
|
|
breaking change that breaks other tenants' maps. Keep all map-specific, non-parameterized content in your
|
|
plugin-design; contribute to `library/` only when it is genuinely reusable and parameterized.
|
|
|
|
---
|
|
|
|
## More docs
|
|
|
|
| Read this | When |
|
|
|---|---|
|
|
| **[docs/develop-a-plugin-and-see-it-live.md](./docs/develop-a-plugin-and-see-it-live.md)** | ★ START HERE — the golden path, zero → a plugin live, in 6 steps |
|
|
| **[docs/README-SECTION-3-cli-commands.md](./docs/README-SECTION-3-cli-commands.md)** | the full per-method CLI + install matrix |
|
|
| **[docs/why.md](./docs/why.md)** | 2 min — the problem, the idea, one diagram |
|
|
| **[docs/tutorial.md](./docs/tutorial.md)** | build your first map, step by step |
|
|
| **[docs/how-to.md](./docs/how-to.md)** | task recipes (embed, data, mobile) |
|
|
| **[docs/README-longform.md](./docs/README-longform.md)** | the original story-first README (the "why maps" manifesto) |
|
|
| **[docs/architecture/](./docs/architecture/)** | the deep end — embeddability, app-wrappers, file map |
|
|
|
|
---
|
|
|
|
> **Bottom line: maps should be open source, run everywhere, and multiply.** This bundler makes each map a
|
|
> self-contained file that embeds on your site, your partners' sites, WordPress, and mobile — from one build.
|