Doxa Maps — the map bundler, 7 production plugins and the Experimental shelf
4
.env.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Mapbox token used in dev. Production hosts pass the token via profile-config.tk
|
||||
# on the host page; never bake into the IIFE bundle.
|
||||
VITE_MAPBOX_TOKEN=pk.your_dev_token_here
|
||||
VITE_API_BASE_URL=https://pray.doxa.life
|
||||
37
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Normalise line endings on commit. Everything here is authored on LF; declaring it
|
||||
# keeps a Windows checkout from rewriting whole files and producing diffs that are
|
||||
# entirely invisible whitespace.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Binaries git must never line-ending-convert or try to diff. The repo carries ~77 MB
|
||||
# of poster imagery, so a wrong guess here corrupts art rather than reformatting text.
|
||||
*.webp binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.ico binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.ttf binary
|
||||
*.jar binary
|
||||
*.jks binary
|
||||
*.keystore binary
|
||||
*.apk binary
|
||||
*.aab binary
|
||||
|
||||
# Executables keep LF regardless of platform, or the shebang breaks.
|
||||
*.sh text eol=lf
|
||||
gradlew text eol=lf
|
||||
|
||||
# ── What GitHub should NOT count as this project's source ────────────────────
|
||||
# public/ is the served web-root and is committed, which means 4.1 MB of the repo's
|
||||
# 4.9 MB of JavaScript is BUILT bundles. Left alone, the language bar reports this
|
||||
# as a pile of minified JS instead of the Vue and JS someone actually maintains,
|
||||
# and the generated staging pages do the same to HTML.
|
||||
public/doxa-maps/** linguist-generated=true
|
||||
library/scripts/*/snapshots/** linguist-generated=true
|
||||
bun.lock linguist-generated=true -diff
|
||||
|
||||
# Committed research data and imagery: real content, but not code anyone reads.
|
||||
public/assets/** linguist-vendored=true
|
||||
plugin-designs/*/data/** linguist-vendored=true
|
||||
88
.github/workflows/pages.yml
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# Build the served web-root and publish it to GitHub Pages.
|
||||
#
|
||||
# public/ is committed, but the bundles inside it are not: public/<name>/ is
|
||||
# regenerated on every push so what ships always matches source. The rest of the
|
||||
# folder — the shell and the pre-generated posters the app fetches at runtime —
|
||||
# comes straight from the checkout.
|
||||
#
|
||||
# MAPBOX_TOKEN is a repository secret. The build turns it into the static token
|
||||
# endpoint at public/api/maps/token.json, which is gitignored, so the credential
|
||||
# reaches the deployed site without ever entering git history. Rotating it means
|
||||
# changing the secret and re-running this workflow.
|
||||
name: Deploy to GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
# Read the repo, write a Pages deployment, and mint the OIDC token Pages requires.
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Never let two deploys race; let a newer push supersede an in-flight one.
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- run: bun install --frozen-lockfile
|
||||
|
||||
# Emits public/<name>/ plus the token endpoint, then runs the build guards
|
||||
# (drop-portability, inline-parse, bundles-shipped and the rest). A guard
|
||||
# failure fails the deploy, which is the point of having them.
|
||||
- name: Build
|
||||
env:
|
||||
MAPBOX_TOKEN: ${{ secrets.MAPBOX_TOKEN }}
|
||||
run: bun run build
|
||||
|
||||
# Fail loudly rather than publishing a site whose maps cannot draw.
|
||||
- name: Assert the token endpoint exists
|
||||
run: |
|
||||
test -s public/api/maps/token.json || {
|
||||
echo "::error::public/api/maps/token.json is missing or empty — set the MAPBOX_TOKEN repository secret, or every basemap will render blank."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# A public web-root must never carry the local staging credential.
|
||||
- name: Assert no local credential is being published
|
||||
run: |
|
||||
if [ -e public/token.local.js ]; then
|
||||
echo "::error::public/token.local.js is present in the web-root and must not be published."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Artefact checks cannot tell whether the site WORKS. This boots the built
|
||||
# public/ in a real browser and fails if the shell cannot resolve a token,
|
||||
# no bundle registers its element, or any same-origin request 404s.
|
||||
- name: Install the browser the smoke test drives
|
||||
run: bunx playwright install --with-deps chromium
|
||||
|
||||
- name: Smoke-test the built site
|
||||
run: bun run smoke
|
||||
|
||||
- uses: actions/configure-pages@v6
|
||||
- uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: public
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
136
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# (The build no longer uses an intermediate app/ tree — vite writes straight into
|
||||
# public/<name>/, which is committed. See "THE SERVED WEB-ROOT" below.)
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
.npm/
|
||||
.pnpm-store/
|
||||
|
||||
# Vite cache
|
||||
.vite/
|
||||
dist/
|
||||
|
||||
# Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
!.env.example
|
||||
|
||||
# Research dashboard — developed on its own branch (research-dashboard),
|
||||
# excluded from the map-only bundle. Do not track dashboard files here.
|
||||
|
||||
# Local build artifacts / deploy config — kept on disk, out of the map PR
|
||||
build-sizes-baseline.json
|
||||
|
||||
|
||||
_archive
|
||||
|
||||
# ── BUILD OUTPUT — regenerated by `bun run build`; one tree, three consumers ──
|
||||
# Not committed: it is derived. Railway builds it in CI; Capacitor copies it via
|
||||
# `cap sync`; staging serves it locally.
|
||||
|
||||
# ── Capacitor generated / machine-specific (the native projects THEMSELVES are
|
||||
# committed under capacitor/; only their build output is ignored) ──
|
||||
capacitor/android/app/src/main/assets/public/
|
||||
capacitor/android/app/src/main/assets/capacitor.config.json
|
||||
capacitor/android/app/src/main/assets/capacitor.plugins.json
|
||||
capacitor/android/build/
|
||||
capacitor/android/app/build/
|
||||
capacitor/android/.gradle/
|
||||
capacitor/android/local.properties
|
||||
capacitor/android/keystore.properties
|
||||
capacitor/ios/App/build/
|
||||
capacitor/ios/App/Pods/
|
||||
*.jks
|
||||
*.keystore
|
||||
|
||||
# ── THE SERVED WEB-ROOT — COMMITTED ───────────────────────────────────────────
|
||||
# public/ is the product: the shell, the built bundles and the pre-generated
|
||||
# posters the app fetches at runtime. It is committed whole so a clone (or a
|
||||
# static host, or the source-control service) has a working site without running
|
||||
# anything, and so CI regenerates only public/<name>/ in place rather than
|
||||
# assembling the folder from other directories.
|
||||
# The exceptions are BOTH Mapbox credentials: token.local.js (local staging, held
|
||||
# out by public/.gitignore) and api/maps/token.json, which the build writes from
|
||||
# the MAPBOX_TOKEN environment variable so the value lives in deployment settings
|
||||
# rather than in git history.
|
||||
public/api/maps/token.json
|
||||
# ★ AND the EXTENSIONLESS path — the client requests /api/maps/token (never .json), so
|
||||
# write-token.mjs emits BOTH. This line was missing when the bare file was added, which
|
||||
# would have made a live pk. token committable. Same secret, second path.
|
||||
public/api/maps/token
|
||||
|
||||
# ── stale dev-time bundle copies inside the wrapper ───────────────────────────
|
||||
# The wrapper mounts bundles from ./doxa-maps/ at the served root now; these were
|
||||
# hand-copied during bring-up and would silently go stale.
|
||||
layouts/*/profiles/
|
||||
layouts/*/js/
|
||||
|
||||
# ── POSTER GENERATOR OUTPUT ───────────────────────────────────────────────────
|
||||
# library/scripts/posters/ is source (the template + its snapshots); out/ is what
|
||||
# it renders — ~95 MB of HTML and 3000x3000 PNGs, all reproducible from source:
|
||||
# bun run posters && bun run posters:shoot && bun run posters:gallery
|
||||
|
||||
# ── EXCEPT the three inputs the build guards read ────────────────────────────
|
||||
# iso2-map, resource-join and tables-complete verify shipped data against these.
|
||||
# They are derived, but they are derived from NETWORK FETCHES, and the research
|
||||
# network policy forbids CI reaching out to those sources — so CI cannot rebuild
|
||||
# them and, without them committed, three guards fail on a clean clone and the
|
||||
# deploy dies. 2 MB of the 23 MB out/ tree; the rest stays ignored.
|
||||
# Visual-gate screenshots are OUTPUT, not fixtures. visual-gate.mjs and
|
||||
# palette-preview.mjs only ever page.screenshot() INTO this folder — the gate's own
|
||||
# rule is that it never reads a PNG back, it asserts against the live DOM. So these
|
||||
# are 11 MB of regenerable pictures of somebody's screen, and they bake build-slice
|
||||
# vocabulary into pixels where no text scrub can reach it.
|
||||
plugin-designs/*/data/visual-gate-shots/
|
||||
|
||||
library/scripts/*/out/**
|
||||
internal/*/out/**
|
||||
# EXCEPT the three inputs the build guards read. iso2-map, resource-join and
|
||||
# tables-complete verify shipped data against these. They are derived — but derived
|
||||
# from NETWORK FETCHES, and the research network policy forbids CI reaching those
|
||||
# sources, so CI cannot rebuild them. Without them committed, three guards fail on a
|
||||
# clean clone and the deploy dies. 2 MB of the 23 MB out/ tree; the rest stays out.
|
||||
# (Negations must follow the exclude: the last matching rule wins.)
|
||||
!library/scripts/research/out/arclight-languages.json
|
||||
!library/scripts/research/out/iso3-to-iso2.json
|
||||
!library/scripts/research/out/language-resources.json
|
||||
!library/scripts/research/out/ebible-translations.csv
|
||||
!library/scripts/research/out/tables/
|
||||
!library/scripts/research/out/tables/**
|
||||
|
||||
# ── INTERNAL DOCS — the split-plane rule ──────────────────────────────────────
|
||||
# Agent/internal working docs live here and are NEVER published. The repo's
|
||||
# public surface stays architecture + code + contributor docs; internal process
|
||||
# notes, lessons-learned and agent instructions stay out of it.
|
||||
internal-tracker/
|
||||
|
||||
# ── GENERATOR OUTPUT — regenerable render artifacts, never committed ──────────
|
||||
# internal/<tool>/out/ holds what the poster/research generators RENDER
|
||||
# (upg100-posters/out alone is ~619MB / 12,623 PNGs). The generators themselves
|
||||
# are source and ARE committed; their output is derived.
|
||||
#
|
||||
# ⚠ THIS BLOCK USED TO SAY "what actually ships is the exported subset in
|
||||
# public/assets/ (see export-public.mjs)". BOTH HALVES OF THAT WERE STALE, and
|
||||
# the sentence is why 80.7 MB sat committed twice:
|
||||
# - export-public.mjs does NOT write public/assets/. It writes a SIBLING OF THE
|
||||
# REPO (../../../../upg-100-posters, or $DOXA_PUBLIC), and says so in its own
|
||||
# header — explicitly "NOT inside public/, because capacitor packages public/
|
||||
# whole and these are 52 MB that must not ship in the APK".
|
||||
# - public/assets/<name> is now a GENERATED MIRROR of the tracked data/<name>,
|
||||
# rewritten by generate-tree.mjs on every `bun run tree`. It is derived, not
|
||||
# exported, so it is ignored in public/.gitignore — which carries the
|
||||
# measurements and the one exception (previews/, which has no data/ source
|
||||
# and must stay tracked).
|
||||
# The tracked source for shipped assets is data/<name>.
|
||||
|
||||
# Private datasets never live in this repo (see DOXA_PRIVATE_DIR in
|
||||
# library/scripts/research/build-tabulator-tables.mjs). Guard against re-creation.
|
||||
data-private/
|
||||
201
LICENSE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2026 The Doxa Maps Starter Kit contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
558
README.md
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
# Goal
|
||||
|
||||
Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His paths straight.
|
||||
|
||||
**Isaiah 40:3 “...Prepare the way for the LORD in the wilderness; make a straight highway for our God in the desert."**
|
||||
|
||||
# dx-maps — one build, maps that embed everywhere
|
||||
|
||||
**Write a map once in `plugin-designs/<your-map>/`, run one build, and drop it on any site** —
|
||||
iframe, script embed, mobile app — or open every map at once as one app. Five plugin
|
||||
capabilities, five deployment methods, one shared library, one build.
|
||||
|
||||
| What dx-maps set out to do | Proof |
|
||||
|---|---|
|
||||
| **One build → one folder** you can drop on any host | `bun run check:drop` |
|
||||
| **Every map embeddable** from a copy-paste snippet | [deployment methods](#the-5-deployment-methods) · `bun run check:embed-targets` |
|
||||
| **Every map interoperable** through props and events | `bun run verify:interop` |
|
||||
| **One app nests every map** — and each stays embeddable on its own | [capability 5](#capability-5--app-shell-the-research-gallery) · `bun run check:composite-reg` |
|
||||
| **5 capabilities × 5 deployment methods** | `bun run verify:5-caps` |
|
||||
| **Shared library, private plugins** — reuse without breaking each other | `bun run check:library-index` |
|
||||
| **One agent per plugin, one shepherd, one working `main`** | [agent swarm](#build-it-with-an-agent-swarm) |
|
||||
|
||||
*Click any ▸ to read more. Everything below is folded so you can scroll the pictures first.*
|
||||
|
||||
---
|
||||
|
||||
## The 7 production maps
|
||||
|
||||
**Seven maps DX uses today** — each one a plugin you can embed on its own, and all seven together in
|
||||
the Research Gallery.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-big-picture.png" alt="Big Picture"><br><b>Big Picture</b><br><sub>the whole research dashboard: filters, map, charts, people groups</sub></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-simple-research.png" alt="Simple Research"><br><b>Simple Research</b><br><sub>the research map with the people-groups grid, made for phones</sub></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-regions.png" alt="Regions"><br><b>Regions</b><br><sub>every region and its countries, then a dashboard per region</sub></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/cap4-upg100-mfe.png" alt="UPG-100 List"><br><b>UPG-100 List</b><br><sub>prayer lists of 100 people groups, with posters to print</sub></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/cap2-prayer.png" alt="Prayer Progress"><br><b>Prayer Progress</b><br><sub>where prayer is happening, and where it is still needed</sub></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-cities-internet.png" alt="Cities with Internet"><br><b>Cities with Internet</b><br><sub>internet access where unreached people groups live</sub></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-gospel-resources.png" alt="Gospel Resources"><br><b>Gospel Resources</b><br><sub>which people groups have scripture, film or audio — and which have nothing</sub></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/cap5-doxa-maps-spa.png" alt="Research Gallery"><br><b>Research Gallery</b><br><sub>all seven in one app — capability 5</sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### On a phone
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-simple-research-phone.png" alt="Simple Research on a phone"><br><b>Simple Research</b></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-regions-phone.png" alt="Regions on a phone"><br><b>Regions</b></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/deploy2-mobile-upg100.png" alt="UPG-100 List on a phone"><br><b>UPG-100 List</b></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-prayer-phone.png" alt="Prayer Progress on a phone"><br><b>Prayer Progress</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-cities-internet-phone.png" alt="Cities with Internet on a phone"><br><b>Cities with Internet</b></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-gospel-resources-phone.png" alt="Gospel Resources on a phone"><br><b>Gospel Resources</b></td>
|
||||
<td width="25%" valign="top"><img src="documentation/readme-images/prod-research-gallery-phone.png" alt="Research Gallery on a phone"><br><b>Research Gallery</b></td>
|
||||
<td width="25%" valign="top"><b>Big Picture</b><br><sub>a desktop dashboard by design — on a phone, Simple Research is the dashboard, and the Research Gallery opens on it.</sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Every capability ships every way
|
||||
|
||||
| Capability | Example | iframe | Mobile app | JavaScript embed | WordPress / Disciple Tools | Desktop |
|
||||
|---|---|:-:|:-:|:-:|:-:|:-:|
|
||||
| **5 — app shell** | Research Gallery | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **4 — composite** | UPG-100 List | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **3 — multi-view** | research map | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **2 — presets** | Prayer Progress | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **1 — single element** | all people groups | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
|
||||
<sub>✅ works today · 🧪 proven, not yet in production. Every plugin builds into the same folder, and that folder is what all five methods ship — so a capability never limits where a plugin can go.</sub>
|
||||
|
||||
### The Experimental shelf
|
||||
|
||||
**New plugins the Research Gallery picks up by itself** — each builds and embeds like any other,
|
||||
and graduates to a named tab when it's ready.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top"><img src="documentation/readme-images/exp-wall-of-people-groups.png" alt="Wall of People Groups"><br><b>Wall of People Groups</b><br><sub>all 2,107 Doxa people groups as photo cards on one zoomable prayer wall, with a pray dialog</sub></td>
|
||||
<td width="50%" valign="top"><img src="documentation/readme-images/exp-prayer-walk-planner.png" alt="Prayer Walk Planner"><br><b>Prayer Walk Planner</b><br><sub>plan a prayer walk through a zone, open it full screen from any embed, and print a walking map that stays sharp when zoomed</sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Also on the shelf: UPG neighbor, all people groups, country posters, the vision card, and the PPLR data maps.</sub>
|
||||
|
||||
<details>
|
||||
<summary><b>How one build becomes five ways to ship — the "map cruncher"</b></summary>
|
||||
|
||||
```text
|
||||
map · map · map · map · map · map · map many maps, each once its own codebase
|
||||
\ \ | | | / /
|
||||
+----- plugin-designs/ ------+ one folder per plugin — the free zone
|
||||
|
|
||||
library/ (@map) shared, reusable pieces — the high bar
|
||||
|
|
||||
ONE build step
|
||||
|
|
||||
public/doxa-maps/ one drop-anywhere folder + manifest.json
|
||||
/ | \ \ iframe mobile JS embed WordPress desktop
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## The 5 plugin capabilities
|
||||
|
||||
A capability is **how much a plugin nests**, from one element (1) to an app of apps (5). Every
|
||||
screenshot is a real plugin from this repo's build.
|
||||
|
||||
### Capability 5 — app shell: the Research Gallery
|
||||
|
||||
**One plugin that holds every map as a tab** — and each tab is still a plugin you can embed on its own.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about capability 5 — the app shell</b></summary>
|
||||
|
||||
An app shell is the outer frame of a whole app: a rail of tabs down the side, a header, and
|
||||
navigation. Each tab loads a **real plugin** from a lower capability, only when you open it, so the
|
||||
app stays fast. Nothing is copied into the shell: when a plugin improves, the shell shows the
|
||||
improvement on the next build.
|
||||
|
||||
- **Where it lives:** `plugin-designs/doxa-maps-spa/` — one element, `<doxa-maps-spa>`.
|
||||
- **Changing the tabs:** the order and grouping are one list, `NAV_MODEL` in `doxa-maps-spa.vue`.
|
||||
- **New plugins show up by themselves:** any plugin that isn't placed in a section lands on the
|
||||
folded **Experimental** shelf, read from the built `manifest.json` — no code to edit.
|
||||
- **A second app shell ships too:** the Plugin Studio (below),
|
||||
which holds *every* plugin for developers.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>What the Research Gallery is made of — seven production maps, an Experimental shelf, all five capabilities in one plugin</b></summary>
|
||||
|
||||
```text
|
||||
Research Gallery <doxa-maps-spa> capability 5
|
||||
├─ Dashboards
|
||||
│ ├─ Big Picture dashboard-plus-grid capability 4
|
||||
│ │ ├─ research dashboard research-dashboard capability 3
|
||||
│ │ └─ people-groups grid people-groups-grid capability 2
|
||||
│ ├─ Simple Research research-map-plus-grid capability 4
|
||||
│ │ ├─ research map doxa-research-map capability 3
|
||||
│ │ └─ people-groups grid people-groups-grid capability 2
|
||||
│ └─ Regions regions-mfe capability 4
|
||||
│ ├─ regions list regions-list capability 1
|
||||
│ └─ region dashboards region-dashboards capability 1
|
||||
├─ Prayer Mobilization
|
||||
│ ├─ UPG-100 List upg-100-mfe capability 4
|
||||
│ │ ├─ list upg-100-list capability 1
|
||||
│ │ └─ poster wall poster-wall capability 1
|
||||
│ └─ Prayer Progress doxa-simple-map capability 2
|
||||
├─ Gospel Research
|
||||
│ ├─ Cities with Internet connectivity-map capability 1
|
||||
│ └─ Gospel Resources resources-map capability 1
|
||||
└─ Experimental (a folded shelf, filled automatically from manifest.json)
|
||||
├─ prayer walk planner prayer-walk-planner capability 1
|
||||
├─ UPG neighbor upg-neighbor capability 3
|
||||
├─ all people groups all-people-groups capability 1
|
||||
├─ country posters country-posters capability 4
|
||||
├─ vision card doxa-vision-card capability 2
|
||||
├─ PPLR data maps pplr-data-maps capability 3
|
||||
└─ Wall of People Groups wall-of-people-groups capability 3
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Capability 4 — composite: plugins wired together
|
||||
|
||||
**Several plugins glued into one**, talking to each other — and each piece still embeddable alone.
|
||||
|
||||

|
||||
|
||||
| List (capability 1, embeddable alone) | Poster wall (capability 1, embeddable alone) |
|
||||
|---|---|
|
||||
|  |  |
|
||||
|
||||
<details>
|
||||
<summary><b>More about capability 4 — composites</b></summary>
|
||||
|
||||
A composite takes plugins that already exist and places them together — side by side, stacked, or
|
||||
as views you switch between — and ships the set as **one new element**. The pieces talk through
|
||||
**props** (settings passed in) and **events** (messages sent out): click a region in one piece and
|
||||
another piece updates. Nothing is copied, so each piece can still be embedded on its own.
|
||||
|
||||
- **Example:** `plugin-designs/my-upg-100-list/` — the UPG-100 list and its poster wall, joined into
|
||||
`upg-100-mfe`. Big Picture, Simple Research and Regions are composites too.
|
||||
- **How to declare one:** a `<name>.meta.json` beside it with `"type": "nested-elements-mfe"` and a
|
||||
`"composes"` list of the plugins inside.
|
||||
- **The layouts that do the placing:** `VerticalTabsLayout` and `StackLayout` in `library/layouts/`.
|
||||
|
||||
</details>
|
||||
|
||||
### Capability 3 — multi-view: many screens, one element
|
||||
|
||||
**One plugin with several screens inside** — tabs you switch between.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about capability 3 — multi-view</b></summary>
|
||||
|
||||
All the screens live in one plugin, so they share code and data and switch instantly. The trade-off:
|
||||
you embed the whole thing — you can't embed just one of its tabs. When you need that, split the tabs
|
||||
into separate plugins and join them with a composite (capability 4).
|
||||
|
||||
- **Example:** `plugin-designs/doxa-research-map/` — Prayer, Engagement, People Groups, Regions,
|
||||
Languages and Religions in one map.
|
||||
- **How to declare one:** a `<name>.meta.json` with `"type": "monolithic"`.
|
||||
|
||||
</details>
|
||||
|
||||
### Capability 2 — presets: one plugin, many maps
|
||||
|
||||
**The same map code, several ready-made versions** — each with its own page and embed code.
|
||||
|
||||
| Prayer | Engagement | Adoption |
|
||||
|--------|------------|----------|
|
||||
|  |  |  |
|
||||
|
||||
<details>
|
||||
<summary><b>More about capability 2 — presets</b></summary>
|
||||
|
||||
You write one map, then list its versions in a small table: a name and the settings for each. The
|
||||
build turns every row into its own standalone page and embed snippet — same code, different map.
|
||||
Adding a version means adding a row, not writing code.
|
||||
|
||||
- **Example:** `plugin-designs/doxa-simple-map/` — Prayer, Engagement and Adoption from one map.
|
||||
- **How to make one:** add `<name>.instances.json` beside the plugin:
|
||||
`{ "instances": [{ "id": "prayer", "title": "Prayer", "props": { … } }] }`.
|
||||
|
||||
</details>
|
||||
|
||||
### Capability 1 — single element
|
||||
|
||||
**One folder, one map, one tag** you can paste into any page.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about capability 1 — single element</b></summary>
|
||||
|
||||
The simplest plugin, and where every map starts: a folder with three files — the map's `.vue`, an
|
||||
`index.js` that registers its tag, and an `index.html` for previewing it. The build turns it into
|
||||
one custom HTML tag, like `<all-people-groups>`, that works on any web page.
|
||||
|
||||
- **Example:** `plugin-designs/all-people-groups/` — every people group on one map.
|
||||
- **How to make one:** copy `plugin-designs/template-bundle/` to `plugin-designs/<your-map>/` and
|
||||
rename it. The studio finds it automatically.
|
||||
- **Which plugin is the example of each capability** is generated, not remembered:
|
||||
[documentation/reference-capability-exemplars.md](./documentation/reference-capability-exemplars.md).
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## The 5 deployment methods
|
||||
|
||||
### 1. iframe — the prayer map on any site ✅ production
|
||||
|
||||
**Paste one line of HTML and the map appears** — on any website, no coding.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about method 1 — iframe</b></summary>
|
||||
|
||||
An iframe is a window in your page that shows another page — here, the map's own page. It's the
|
||||
easiest way to share a map: it works on any site that accepts HTML, and the map keeps its own look.
|
||||
Every plugin's exact snippet is `embed.iframe` in `public/doxa-maps/manifest.json`:
|
||||
|
||||
```html
|
||||
<iframe src="https://YOUR_HOST/doxa-maps/doxa-simple-map/doxa-simple-map/prayer/index.html?utm_source=YOUR_SOURCE"
|
||||
width="100%" height="600" style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
||||
```
|
||||
|
||||
Host the built `public/doxa-maps/` folder anywhere (it works from any address), then point the
|
||||
iframe at it.
|
||||
|
||||
</details>
|
||||
|
||||
### 2. Mobile app — UPG-100 on a phone ✅ built
|
||||
|
||||
**The same maps, wrapped as a phone app.**
|
||||
|
||||
<p align="center"><img src="documentation/readme-images/deploy2-mobile-upg100.png" alt="Method 2 — the UPG-100 list at phone size" width="320"></p>
|
||||
|
||||
<details>
|
||||
<summary><b>More about method 2 — mobile app</b></summary>
|
||||
|
||||
[Capacitor](https://capacitorjs.com) wraps the built maps into an Android app — the same files,
|
||||
no new code. Shown here: the UPG-100 prayer lists and posters at phone size, each poster with its
|
||||
own print options.
|
||||
|
||||
```bash
|
||||
bun run check:mobile # checks the app is wired up correctly — needs no Android tools
|
||||
bun run phone # builds the app and installs it on a phone or emulator
|
||||
```
|
||||
|
||||
`bun run phone` needs the Android SDK, JDK 17 and `adb`. Only Android is set up; iOS is not yet.
|
||||
|
||||
</details>
|
||||
|
||||
### 3. JavaScript embed — the Research Gallery on dx-life ✅ production
|
||||
|
||||
**Two lines of code put a whole app inside your own page** — here, the Research Gallery on dx-life.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about method 3 — JavaScript embed</b></summary>
|
||||
|
||||
Instead of a window onto another page, the map becomes **part of your page**, so your page can talk
|
||||
to it. The dx-life research page replaced its separate research map, list and regions dashboards
|
||||
with this one embed of the Research Gallery. The exact snippet is `embed.script` in `manifest.json`;
|
||||
copy its `integrity` hash from there, because it changes with every build:
|
||||
|
||||
```html
|
||||
<script>window.MAP_APP_API_URL="https://pray.doxa.life";</script>
|
||||
<script src="https://YOUR_HOST/doxa-maps/doxa-maps-spa/doxa-maps-spa.js"
|
||||
integrity="sha384-…" crossorigin="anonymous"></script>
|
||||
<doxa-maps-spa profile-config='{"profile":"doxa-maps-spa","tk":"YOUR_MAPBOX_TOKEN","utmSource":"YOUR_SOURCE","assetsBase":"https://YOUR_HOST/doxa-maps/assets"}'></doxa-maps-spa>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 4. WordPress / Disciple Tools — 🧪 proven, not yet in production
|
||||
|
||||
**We got a JAMstack map running inside WordPress and Disciple Tools.**
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about method 4 — WordPress / Disciple Tools</b></summary>
|
||||
|
||||
It works, but it isn't yet a production feature of the map bundler. Two ways to try it:
|
||||
|
||||
- **Easiest:** paste a method 1 or method 3 snippet into a WordPress **Custom HTML** block.
|
||||
- **As a plugin:** install `deployment/wordpress/doxa-maps-embed/` in `wp-content/plugins/`. It adds
|
||||
a `[doxa_map]` shortcode and a block for the block editor.
|
||||
|
||||
`bun run check:wp-embed` confirms the plugin writes the right HTML, without needing WordPress
|
||||
installed.
|
||||
|
||||
</details>
|
||||
|
||||
### 5. Desktop app (Tauri) — 🧪 proven, not yet in production
|
||||
|
||||
**We got the JAMstack map app running as a desktop app.**
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about method 5 — desktop app</b></summary>
|
||||
|
||||
[Tauri](https://tauri.app) wraps the same built maps into a desktop program: it produced a Linux
|
||||
app and a `.deb` installer. It isn't in the dx bundler for production yet — and the Research Gallery
|
||||
could become a strong desktop app.
|
||||
|
||||
```bash
|
||||
bun run check:tauri # checks the desktop setup — needs no Rust tools
|
||||
bun run tauri:build # builds the app — needs Rust, the Tauri CLI and webview libraries
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<sub>WordPress and Tauri marks from [Simple Icons](https://simpleicons.org) (CC0); the trademarks belong to their owners.</sub>
|
||||
|
||||
---
|
||||
|
||||
## Build it with an agent swarm
|
||||
|
||||
**Many AI agents can build many plugins at once without breaking each other's work.** Every plugin
|
||||
has one agent building it, and one shepherd agent makes sure every agent does its job well. All the
|
||||
plugins live in one project with one shared library, and changes are added to `main` — the one
|
||||
official version — one at a time.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>What problem does this solve? (in plain words)</b></summary>
|
||||
|
||||
All the maps here share **one project, one library and one build step**. That's what makes them
|
||||
work together, and it's also the danger: if one agent's change breaks the shared library, **every
|
||||
map breaks at once**. And when several agents work in the same project at the same time, they step on
|
||||
each other — two agents edit the same file, one change quietly erases another, or half-finished work
|
||||
lands in the version everyone else is using.
|
||||
|
||||
So the project works like a busy kitchen with one head chef:
|
||||
|
||||
1. **Each agent works in its own copy of the project** — its own counter, so nobody bumps anybody.
|
||||
2. **Before adding its work, each agent catches up**: it pulls in everyone else's latest work and
|
||||
fixes any clashes with its own. The agent fixes the clash, not the next person.
|
||||
3. **Only one agent adds work at a time.** They wait in a line (the *merge queue*), the checks run,
|
||||
and only then does the change go into `main`. `main` always works.
|
||||
4. **A shepherd agent watches everyone**: plugins built in the studio, shared parts really shared,
|
||||
and every change going through the line.
|
||||
|
||||
That's how many plugins get built at the same time, in one project, without the maps breaking.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>The words, if you're new to git</b></summary>
|
||||
|
||||
| Word | In plain terms |
|
||||
|---|---|
|
||||
| **repo** | the project folder, with its full history |
|
||||
| **main** | the one official version of the project that everyone builds from |
|
||||
| **worktree** | a private copy of the project for one agent to work in |
|
||||
| **merge conflict** | two changes touched the same lines; someone has to decide how to combine them |
|
||||
| **rebase** | catching your work up with everyone else's latest before adding it |
|
||||
| **merge queue** | the line agents wait in, so only one change goes into `main` at a time |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>The commands (for the curious)</b></summary>
|
||||
|
||||
```bash
|
||||
# one private copy (worktree) + one agent per plugin
|
||||
git worktree add -b regions/work ../dx-maps-regions main
|
||||
git worktree add -b upg100/work ../dx-maps-upg100 main
|
||||
|
||||
# one preview server per agent, each on its own port
|
||||
bun x vite --port 5301 --strictPort
|
||||
|
||||
# add work through the line: one at a time, caught up first, checks must pass
|
||||
flock /tmp/dx-maps-main.lock sh -c '
|
||||
git fetch origin && git rebase origin/main &&
|
||||
bun run gates:pre && git push origin HEAD:main'
|
||||
```
|
||||
|
||||
One agent owns the full build (it rebuilds everyone's output); the others check just their own
|
||||
plugin with `BUNDLE=<name> bun x vite build`. How to contribute, and the bar for shared code:
|
||||
[documentation/CONTRIBUTING.md](./documentation/CONTRIBUTING.md). Diagram source:
|
||||
[`delegation-model.svg`](./documentation/readme-images/src/delegation-model.svg).
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## The Plugin Studio — ★ where you work
|
||||
|
||||
**Every plugin in one place, sorted by capability.** Pick any plugin to run it live, fold open the
|
||||
plugins inside it, and copy its embed code. Tell your agent which plugins to **group**, and it builds
|
||||
a higher-capability plugin from them — that's how the Research Gallery was built.
|
||||
|
||||

|
||||
|
||||
### Get it running
|
||||
|
||||
You need **git**, **[Bun](https://bun.sh)** and **Node ≥ 18**, plus a free
|
||||
**[Mapbox](https://account.mapbox.com) public token** (it starts with `pk.`) so the maps have a
|
||||
background.
|
||||
|
||||
```bash
|
||||
git clone https://git.nuxtbase.app/doxa-open/doxa-maps.git
|
||||
cd doxa-maps
|
||||
bun install --frozen-lockfile
|
||||
cp .env.example .env # then paste your pk. token after VITE_MAPBOX_TOKEN=
|
||||
bun run dev # → http://localhost:5173/ is the Plugin Studio
|
||||
```
|
||||
|
||||
No build needed first — the built maps are already in the repo. To start a plugin, copy
|
||||
`plugin-designs/template-bundle/` to `plugin-designs/<your-map>/`; the studio finds it automatically.
|
||||
|
||||
<details>
|
||||
<summary><b>Make changes to the studio itself</b></summary>
|
||||
|
||||
The studio is a plugin like any other: `plugin-designs/plugin-studio/plugin-studio.vue`.
|
||||
|
||||
1. Run `bun run dev` and open http://localhost:5173/ — that page *is* the studio.
|
||||
2. Edit `plugin-studio.vue` (or ask your agent to). The page updates as you save.
|
||||
3. When it looks right, build just the studio and refresh its security hashes:
|
||||
|
||||
```bash
|
||||
BUNDLE=plugin-studio bun x vite build
|
||||
bun run fix:sri
|
||||
```
|
||||
|
||||
4. Check it opens every plugin: `bun run smoke:studio` (with `bun run dev` running).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>No Mapbox token? And other map settings</b></summary>
|
||||
|
||||
- **No token:** set `"provider": "maplibre"` on any plugin (or add `?provider=maplibre` to a map's
|
||||
page) and it draws with MapLibre, which needs no key.
|
||||
- **Built pages** take a token from `?tk=pk.…` in the address, from a token you paste once into the
|
||||
page, or from a token endpoint.
|
||||
- Every command and prerequisite, per deployment method:
|
||||
[documentation/README-SECTION-3-cli-commands.md](./documentation/README-SECTION-3-cli-commands.md).
|
||||
|
||||
</details>
|
||||
|
||||
| Command | What it does |
|
||||
|---|---|
|
||||
| `bun run dev` | The Plugin Studio, updating as you save, at http://localhost:5173 |
|
||||
| `BUNDLE=<name> bun x vite build` | Build one plugin only |
|
||||
| `BUILD_ALLOW=1 bun run build` | Build everything into `public/doxa-maps/` (~4 min; refuses without the flag, to protect a shared project) |
|
||||
| `bun run serve` | Serve the built maps at http://localhost:4173 |
|
||||
| `bun run gates:pre` · `bun run gates:post` | The checks that keep `main` working |
|
||||
| `bun run shots` | Retake these screenshots (`ONLY=<name>` for one) |
|
||||
|
||||
---
|
||||
|
||||
## Repository layout
|
||||
|
||||
```text
|
||||
doxa-maps/
|
||||
├── plugin-designs/ YOUR maps — one folder per plugin (the free zone)
|
||||
│ ├── doxa-maps-spa/ the Research Gallery (capability 5)
|
||||
│ ├── plugin-studio/ the Plugin Studio (capability 5)
|
||||
│ ├── my-upg-100-list/ UPG-100 List (capability 4)
|
||||
│ ├── doxa-simple-map/ Prayer Progress and its presets (capability 2)
|
||||
│ ├── all-people-groups/ a single map (capability 1)
|
||||
│ └── template-bundle/ copy this to start a new plugin
|
||||
│ ├── template-map-a.vue the map itself
|
||||
│ ├── index.js registers its HTML tag
|
||||
│ ├── index.html a preview page
|
||||
│ └── src/ private pieces only this plugin uses
|
||||
├── library/ the SHARED toolkit, imported as @map (the high bar)
|
||||
│ ├── layouts/ VerticalTabsLayout, StackLayout — how composites compose
|
||||
│ ├── components/ legends, map controls, search, posters, detail panels
|
||||
│ ├── composables/ map instance, data, layers, shared helpers
|
||||
│ ├── colors/ styles/ colour strategies and the house style
|
||||
│ ├── i18n/locales/ the 11 languages
|
||||
│ └── data/ api/ lookup tables and data sources
|
||||
├── public/doxa-maps/ the BUILD OUTPUT — the drop-anywhere folder + manifest.json
|
||||
├── deployment/ mobile (Capacitor) · WordPress · desktop (Tauri)
|
||||
├── internal/ the build and its checks
|
||||
└── documentation/ tutorial · how-to · reference · architecture
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Where your code goes — a plugin, or the shared library?</b></summary>
|
||||
|
||||
- **Write your app in `plugin-designs/<your-map>/`.** On the build step it becomes its own plugin in
|
||||
the output folder — its own embeddable app. Nothing you do there can break anyone else's map.
|
||||
- **Only put something in `library/` if it is genuinely reusable *and* parameterized** — built so
|
||||
other maps can use it with their own settings. The library is shared by every map, so every edit
|
||||
there can break every map: it is a high bar. Keep anything specific to one map inside that map's
|
||||
own folder.
|
||||
- The full contribution guide: [documentation/CONTRIBUTING.md](./documentation/CONTRIBUTING.md) ·
|
||||
every reusable piece you can import: [documentation/reference-library-index.md](./documentation/reference-library-index.md).
|
||||
|
||||
</details>
|
||||
|
||||
> **Maps should be open source, run everywhere, and multiply.** One build makes each map a
|
||||
> self-contained file for your site, your partners' sites and a phone. The Plugin Studio is where
|
||||
> you compose them into something bigger.
|
||||
BIN
bun.lock
generated
Normal file
39
capacitor.config.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appId": "life.doxa.maps",
|
||||
"appName": "Doxa Maps",
|
||||
"//webDir": [
|
||||
"webDir = ./public \u2014 THE CDN WEB-ROOT. It is a POINTER, NOT A COPY.",
|
||||
"public/ is the single SERVED SURFACE, so the bundler IS the CDN: it serves its own bundles AND",
|
||||
"non-bundle assets (brand/logos, vendored libs, fonts) that belong to no single bundle.",
|
||||
" public/doxa-maps/<bundle>/ built bundles (vite outDir app/ + copied here by the build config's paths)",
|
||||
" public/assets/brand/ logos and brand imagery",
|
||||
" public/assets/vendor/ self-hosted libs (e.g. mapbox-gl) \u2014 no CDN, per the no-CDN rule",
|
||||
" public/assets/fonts/ Bebas Neue / Poppins (both SIL OFL, so self-hostable)",
|
||||
"ONE build, THREE consumers, all reading THIS root:",
|
||||
" 1. Railway / CDN \u2014 serve public/ statically; partner sites embed from stable URLs",
|
||||
" 2. Capacitor \u2014 this webDir; `cap sync` copies public/ into deployment/capacitor/android/app/src/main/assets/public/",
|
||||
" 3. local staging \u2014 `bun run serve` (public/ on :4173)",
|
||||
"NOTE the drop-portability gate still governs what goes INSIDE a bundle: bundle-internal refs must stay",
|
||||
"RELATIVE. public/ is the HOST root, which is a different layer \u2014 putting shared assets here does not",
|
||||
"make a bundle root-relative, so the gate stays green.",
|
||||
"\u26a0\ufe0f SOFT FAILURE: a wrong webDir yields an EMPTY assets/public and a BLANK WHITE SCREEN at launch, not an",
|
||||
"error. After every sync, verify deployment/capacitor/android/app/src/main/assets/public/index.html exists (`bun run cap:verify`)."
|
||||
],
|
||||
"webDir": "public",
|
||||
"server": {
|
||||
"//": [
|
||||
"Capacitor defaults are deliberately kept: iosScheme 'capacitor', androidScheme 'https', hostname",
|
||||
"'localhost'. Do NOT set a custom scheme \u2014 Capacitor docs warn that as of WebView 117 custom schemes",
|
||||
"cannot change the URL path and can break routing. Do NOT set server.url either; that is documented as",
|
||||
"live-reload/dev only, not production.",
|
||||
"CONSEQUENCE FOR US: the app origin is https://localhost, so the doxa.life token endpoint (tokenUrl) must",
|
||||
"send CORS headers permitting that origin. This is the single most likely runtime surprise, and the fix is",
|
||||
"a response header on doxa.life \u2014 not a change here."
|
||||
],
|
||||
"androidScheme": "https"
|
||||
},
|
||||
"android": {
|
||||
"//": "Native project lives in ./capacitor/android and is COMMITTED \u2014 `cap add` scaffolds it ONCE, then it is hand-edited source (manifest, icons, versionCode). It sits under deployment/capacitor/ rather than app/ because app/ is the BUNDLE OUTPUT tree: this repo emits several kinds of product, and Capacitor is one facet of them, not the whole. Only app/src/main/assets/public/ is generated and gitignored. An iOS project would join it at deployment/capacitor/ios (ios.path), scaffolded on a Mac.",
|
||||
"path": "deployment/capacitor/android"
|
||||
}
|
||||
}
|
||||
16
data/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# data/ — the bundler's source-of-truth data
|
||||
|
||||
**The law: data lives here; `public/` is generated.** Every folder in `data/` is copied into the
|
||||
served web-root by the build (`internal/generate-tree.mjs`), exactly like the per-profile data in
|
||||
`plugin-designs/*/data/`. You should be able to `rm -rf public/` and get it all back with `bun run build`.
|
||||
|
||||
| Folder | Served at | What it is |
|
||||
|---|---|---|
|
||||
| `people-groups/` | `/assets/people-groups/` | People-group datasets (CSV/JSON) the maps read at runtime |
|
||||
| `vendor/` | `/assets/vendor/` | Third-party runtime assets (e.g. table libraries) |
|
||||
| `posters/` | `/assets/posters/` | Rendered poster webp set (heavy pipeline output; poster walls fetch these) |
|
||||
| `tables/` | `/assets/tables/` | The MERGED served table set (its index.json is the merge; per-profile `plugin-designs/*/data/tables` are build-time sources) |
|
||||
| `external-bundles/` | not served | Prebuilt map IIFEs vendored from sibling projects (not built here); only the optional `layouts/` shell mounted them, so the build no longer copies them into `public/` |
|
||||
|
||||
Don't edit anything under `public/` by hand — change it here (or in `plugin-designs/*/data/`,
|
||||
`layouts/*/assets/`) and rebuild.
|
||||
337
data/external-bundles/research-dashboard-map.js
Normal file
548
data/external-bundles/upg-neighbor.js
Normal file
10541
data/people-groups/doxa-groups-snapshot.json
Normal file
1
data/people-groups/people-groups-points.json
Normal file
1
data/people-groups/people-groups-slim.json
Normal file
12380
data/people-groups/people_groups.csv
Normal file
40
data/people-groups/vision-funnel.json
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"sources": {
|
||||
"imb": {
|
||||
"file": "data/people-groups/people_groups.csv",
|
||||
"publisher": "IMB / peoplegroups.org",
|
||||
"captured": "2026-07-16"
|
||||
},
|
||||
"doxa": {
|
||||
"file": "data/people-groups/doxa-groups-snapshot.json",
|
||||
"source": "https://pray.doxa.life/api/people-groups/list",
|
||||
"fetched": "2026-09-25"
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"id": "all",
|
||||
"groups": 12379,
|
||||
"population": 8288563969
|
||||
},
|
||||
{
|
||||
"id": "unreached",
|
||||
"groups": 7341,
|
||||
"population": 4893057899
|
||||
},
|
||||
{
|
||||
"id": "frontier",
|
||||
"groups": 4754,
|
||||
"population": 2813855464
|
||||
},
|
||||
{
|
||||
"id": "doxa",
|
||||
"groups": 2107,
|
||||
"population": 209079159,
|
||||
"frontier": 1745,
|
||||
"unreached": 2082,
|
||||
"matched": 2091,
|
||||
"unmatched": 16
|
||||
}
|
||||
]
|
||||
}
|
||||
355
data/posters/andean-caribbean.html
Normal file
BIN
data/posters/andean-caribbean.webp
Normal file
|
After Width: | Height: | Size: 217 KiB |
355
data/posters/asia-china-tibet-autonomous-region.html
Normal file
BIN
data/posters/asia-china-tibet-autonomous-region.webp
Normal file
|
After Width: | Height: | Size: 184 KiB |
355
data/posters/asia-china-yunnan-province.html
Normal file
BIN
data/posters/asia-china-yunnan-province.webp
Normal file
|
After Width: | Height: | Size: 235 KiB |
355
data/posters/asia-india-andhra-pradesh.html
Normal file
BIN
data/posters/asia-india-andhra-pradesh.webp
Normal file
|
After Width: | Height: | Size: 230 KiB |
355
data/posters/asia-india-gujarat.html
Normal file
BIN
data/posters/asia-india-gujarat.webp
Normal file
|
After Width: | Height: | Size: 216 KiB |
355
data/posters/asia-india-karnataka.html
Normal file
BIN
data/posters/asia-india-karnataka.webp
Normal file
|
After Width: | Height: | Size: 220 KiB |
355
data/posters/asia-india-kerala.html
Normal file
BIN
data/posters/asia-india-kerala.webp
Normal file
|
After Width: | Height: | Size: 232 KiB |
355
data/posters/asia-india-madhya-pradesh.html
Normal file
BIN
data/posters/asia-india-madhya-pradesh.webp
Normal file
|
After Width: | Height: | Size: 219 KiB |
355
data/posters/asia-india-odisha.html
Normal file
BIN
data/posters/asia-india-odisha.webp
Normal file
|
After Width: | Height: | Size: 219 KiB |
355
data/posters/asia-india-rajasthan.html
Normal file
BIN
data/posters/asia-india-rajasthan.webp
Normal file
|
After Width: | Height: | Size: 240 KiB |
355
data/posters/asia-india-tamil-nadu.html
Normal file
BIN
data/posters/asia-india-tamil-nadu.webp
Normal file
|
After Width: | Height: | Size: 219 KiB |
355
data/posters/asia-india-uttarakhand.html
Normal file
BIN
data/posters/asia-india-uttarakhand.webp
Normal file
|
After Width: | Height: | Size: 223 KiB |
355
data/posters/asia-india-west-bengal.html
Normal file
BIN
data/posters/asia-india-west-bengal.webp
Normal file
|
After Width: | Height: | Size: 224 KiB |
355
data/posters/brazil.html
Normal file
BIN
data/posters/brazil.webp
Normal file
|
After Width: | Height: | Size: 238 KiB |
355
data/posters/central-africa.html
Normal file
BIN
data/posters/central-africa.webp
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
data/posters/city-ago--8.8484-13.2535.webp
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
data/posters/city-bfa-9.7792--4.2879.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
data/posters/city-brn-4.9234-114.9209.webp
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
data/posters/city-btn-27.75-91.2637.webp
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
data/posters/city-cod-3.5049-24.4533.webp
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
data/posters/city-idn--3.7191-127.9542.webp
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
data/posters/city-ind-10.1396-77.7883.webp
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
data/posters/city-ind-10.3796-78.8202.webp
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
data/posters/city-ind-10.5285-76.2146.webp
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
data/posters/city-ind-10.7853-78.7051.webp
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
data/posters/city-ind-10.7855-76.6516.webp
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
data/posters/city-ind-10.787-79.1378.webp
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
data/posters/city-ind-10.8469-76.2633.webp
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
data/posters/city-ind-11.0049-76.9626.webp
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
data/posters/city-ind-11.0546-78.6369.webp
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
data/posters/city-ind-11.06-76.0816.webp
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
data/posters/city-ind-11.0991-77.2807.webp
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
data/posters/city-ind-11.1271-78.6565.webp
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
data/posters/city-ind-11.253-76.2299.webp
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
data/posters/city-ind-11.3548-77.711.webp
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
data/posters/city-ind-11.4517-76.7145.webp
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
data/posters/city-ind-11.6234-92.7265.webp
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
data/posters/city-ind-11.6765-76.1741.webp
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
data/posters/city-ind-11.8745-75.3695.webp
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
data/posters/city-ind-11.9182-79.8085.webp
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
data/posters/city-ind-11.967-75.3663.webp
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
data/posters/city-ind-12.2973-75.7684.webp
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
data/posters/city-ind-12.3022-76.6423.webp
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
data/posters/city-ind-12.427-75.742.webp
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
data/posters/city-ind-12.4538-75.1701.webp
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
data/posters/city-ind-12.5102-74.9852.webp
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
data/posters/city-ind-12.5446-80.0193.webp
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
data/posters/city-ind-12.6867-79.9614.webp
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
data/posters/city-ind-12.8403-75.2483.webp
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
data/posters/city-ind-12.9125-79.3429.webp
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
data/posters/city-ind-12.964-74.837.webp
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
data/posters/city-ind-12.9789-77.5857.webp
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
data/posters/city-ind-13.0505-75.1773.webp
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
data/posters/city-ind-13.2174-79.101.webp
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
data/posters/city-ind-13.2466-80.2533.webp
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
data/posters/city-ind-13.3138-75.7731.webp
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
data/posters/city-ind-13.3389-74.7451.webp
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
data/posters/city-ind-13.4672-79.0137.webp
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
data/posters/city-ind-13.6192-79.4197.webp
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
data/posters/city-ind-14.4428-79.9882.webp
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
data/posters/city-ind-14.494-78.7992.webp
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
data/posters/city-ind-14.7445-74.7282.webp
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
data/posters/city-ind-14.7811-74.6161.webp
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
data/posters/city-ind-14.8066-74.1305.webp
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
data/posters/city-ind-15.1518-75.4446.webp
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
data/posters/city-ind-15.2213-74.0714.webp
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
data/posters/city-ind-15.4573-75.0086.webp
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
data/posters/city-ind-15.4895-79.6006.webp
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
data/posters/city-ind-15.5127-77.9554.webp
Normal file
|
After Width: | Height: | Size: 107 KiB |