merge main before the licence landing
This commit is contained in:
commit
9cd99cba23
28 changed files with 496 additions and 121 deletions
51
.env.example
51
.env.example
|
|
@ -1,16 +1,41 @@
|
|||
# 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
|
||||
# .env.example — every local setting, each shown with its DEFAULT.
|
||||
# A fresh clone needs NONE of this: `bun run dev` works with no .env at all. To change something, copy
|
||||
# this file to .env (or .env.local, which wins; both are gitignored) and edit only the lines you need.
|
||||
# An empty value means "use the default".
|
||||
|
||||
# ── Maps ─────────────────────────────────────────────────────────────────────────────────────────
|
||||
# A Mapbox public token (starts with pk.) for the map backgrounds. Empty = no Mapbox background; set
|
||||
# "provider": "maplibre" on a plugin (or ?provider=maplibre on a page) to draw with MapLibre, no key.
|
||||
# Production hosts pass the token through profile-config.tk; never bake it into a bundle.
|
||||
VITE_MAPBOX_TOKEN=
|
||||
# The data API the maps read when a plugin's profile does not name one (profile apiBaseUrl and a host
|
||||
# page's window.MAP_APP_API_URL come first). This is the value the maps are built against.
|
||||
VITE_API_BASE_URL=https://pray.doxa.life
|
||||
|
||||
# Follow a shared branch while `bun run dev` runs (internal/dev-sync-plugin.mjs): fetch every N seconds and
|
||||
# fast-forward when this checkout is on that branch with no local edits; otherwise only report it at
|
||||
# /api/dev-sync. DX_SYNC_REMOTE is a remote name (origin) or a repo URL; leave it unset to turn this off.
|
||||
# DX_SYNC_REMOTE=origin
|
||||
# DX_SYNC_BRANCH=main
|
||||
# DX_SYNC_INTERVAL=5
|
||||
# ── Hot reload from a shared branch (internal/dev-sync-plugin.mjs) ───────────────────────────────
|
||||
# Empty = OFF: the dev server hot-reloads YOUR local edits only (the default).
|
||||
# Set to a remote name (origin) or a repo URL (https://…/repo.git) and `bun run dev` fetches every few
|
||||
# seconds and fast-forwards when this checkout is on that branch with no local edits or commits; otherwise
|
||||
# it only reports "main is N commits ahead" (in the log and at /api/dev-sync). It never merges over your work.
|
||||
DX_SYNC_REMOTE=
|
||||
# The branch to follow. Empty = main.
|
||||
DX_SYNC_BRANCH=
|
||||
# Seconds between checks. Empty = 5 (minimum 2).
|
||||
DX_SYNC_INTERVAL=
|
||||
|
||||
# PocketBase for plugins (internal/backend-dev-plugin.mjs): `bun run dev` downloads the pinned binary once
|
||||
# and serves it at /api/pb, data in .pb/dev. DX_PB_URL uses a shared PocketBase instead of a local one.
|
||||
# DX_PB_PORT=8090
|
||||
# DX_PB_URL=
|
||||
# ── PocketBase, the backend for plugins (internal/backend-dev-plugin.mjs) ────────────────────────
|
||||
# Empty = a LOCAL PocketBase: `bun run dev` downloads the pinned binary once (checksum-verified) and
|
||||
# serves it at /api/pb, admin UI /api/pb/_/, data in .pb/dev (gitignored). Nothing leaves this computer.
|
||||
# Set to a PocketBase root (e.g. https://<your team's dev server>/pb) to use a shared team one instead.
|
||||
DX_PB_URL=
|
||||
# The local PocketBase's port. Empty = 8090 (change it only if the log says the port is in use).
|
||||
DX_PB_PORT=
|
||||
|
||||
# ── Rudder, voice to text (backend/rudder-engine) ────────────────────────────────────────────────
|
||||
# These are read from the SHELL ENVIRONMENT, not from this file (e.g. `RUDDER_PORT=6193 bun run dev`).
|
||||
# Rudder runs when its engine is built: install Go 1.22+ and run `bash backend/rudder-engine/setup-runtime.sh`.
|
||||
# RUDDER_ENGINE 0 = never start the engine here (default: start it if it is built)
|
||||
# RUDDER_PORT the engine's port (default 5193)
|
||||
# RUDDER_NEMO_PORT the speech server's port (default 5194)
|
||||
# RUDDER_HOME where the runtime and model live (default ~/.local/share/rudder)
|
||||
# RUDDER_LANG the default language (default en-US)
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -4,6 +4,8 @@ Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His
|
|||
|
||||
**Isaiah 40:3 “...Prepare the way for the LORD in the wilderness; make a straight highway for our God in the desert."**
|
||||
|
||||
Built with love, with the prayer that these maps bless the nations.
|
||||
|
||||
# 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** —
|
||||
|
|
@ -646,13 +648,14 @@ a higher-capability plugin from them — that's how the Research Gallery was bui
|
|||
|
||||
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.
|
||||
background. For Rudder's voice engine, also **[Go](https://go.dev/dl/) 1.22+**, then run
|
||||
`bash backend/rudder-engine/setup-runtime.sh` once; without Go, everything except voice works.
|
||||
|
||||
```bash
|
||||
git clone https://git.nuxtbase.app/doxa-open/doxa-maps.git
|
||||
git clone --depth 1 https://git.nuxtbase.app/doxa-open/doxa-maps.git # latest code only: seconds, not hundreds of MB of history
|
||||
cd doxa-maps
|
||||
bun install --frozen-lockfile
|
||||
cp .env.example .env # then paste your pk. token after VITE_MAPBOX_TOKEN=
|
||||
cp .env.example .env # optional: paste your pk. token after VITE_MAPBOX_TOKEN=
|
||||
bun run dev # → http://localhost:5173/ is the Plugin Studio
|
||||
```
|
||||
|
||||
|
|
@ -705,8 +708,9 @@ The studio is a plugin like any other: `plugin-designs/plugin-studio/plugin-stud
|
|||
their browser within seconds, with no rebuild. Add OIDC settings and it puts a "Sign in with *your
|
||||
provider*" page in front of everything, open only to the groups you allow. Recipe and settings:
|
||||
[deployment/cloud/](./deployment/cloud/README.md).
|
||||
Working from your own laptop alongside the team (following the shared branch, the team's PocketBase):
|
||||
[documentation/HOWTO-join-the-table.md](./documentation/HOWTO-join-the-table.md).
|
||||
Working from your own laptop alongside the team (zero config, everything local by default):
|
||||
[documentation/HOWTO-join-the-table.md](./documentation/HOWTO-join-the-table.md). When your agent runs on a
|
||||
server instead: [documentation/CLOUD-DEVELOPMENT-ENVIRONMENT.md](./documentation/CLOUD-DEVELOPMENT-ENVIRONMENT.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,93 @@
|
|||
Bebas Neue — SIL Open Font License, Version 1.1
|
||||
Designer: Ryoichi Tsunekawa (Dharma Type). Distributed via Google Fonts.
|
||||
Full text: https://openfontlicense.org/ (OFL-1.1)
|
||||
Copyright © 2010 by Dharma Type.
|
||||
|
||||
WHY THIS FILE IS IN THIS REPO
|
||||
DX-life's /regions pages set --font-family-heading to 'Bebas Neue'. Their own site does not
|
||||
ship the file either — @nuxt/fonts downloads it at build and self-hosts it, which is why their
|
||||
computed font-family reads "Bebas Neue", "Bebas Neue Fallback: ...". We cannot @font-face from
|
||||
their origin: their font file is served without CORS headers, so a cross-origin face silently
|
||||
fails and every heading fell back to a condensed system stack. That substitution was the ONLY
|
||||
remaining difference between our regions dashboard and theirs (7 computed properties, all of
|
||||
them this one font and its knock-on sizes).
|
||||
So the font is vendored here, from Google Fonts, under the OFL that permits redistribution —
|
||||
self-hosted, never a CDN, the same rule public/assets/vendor/tabulator/ follows.
|
||||
Subsets and unicode-ranges are Google's own (css2 API, v16), reproduced verbatim in the
|
||||
@font-face declared by library/composables/useDocumentFont.js.
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
NOTE ON THIS FILENAME — it is deliberately extensionless.
|
||||
check:public-hygiene refuses *.md / *.markdown / *.txt anywhere in the served tree ("no docs in a
|
||||
served folder", Driver rule 2026-09-16), and it is right to: prose does not belong in a public
|
||||
mirror. But the OFL requires this licence to accompany the font files wherever they are
|
||||
redistributed, and the font files DO ship — they are fetched by the @font-face at runtime. So the
|
||||
licence travels with them under the conventional extensionless name, which satisfies the rule
|
||||
without weakening it. Do not rename this to LICENSE.txt.
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,3 @@
|
|||
Figtree — SIL Open Font License, Version 1.1
|
||||
Designer: Erik D. Kennedy. Distributed via Google Fonts.
|
||||
Vendored as the free stand-in for Brandon Grotesque (commercial), by Driver decision
|
||||
2026-09-24. Chosen by MEASUREMENT, not by name: at 500/19.5312px Figtree renders
|
||||
'FULL PROFILE' at 126px against Brandon's 128px on live doxa.life — the closest of 19
|
||||
libre grotesques tested. See _probe-glyph-width.mjs.
|
||||
|
||||
Copyright 2022 The Figtree Project Authors (https://github.com/erikdkennedy/figtree)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
|
|
|
|||
|
|
@ -1,29 +1,93 @@
|
|||
Poppins — SIL Open Font License, Version 1.1
|
||||
Designers: Indian Type Foundry, Jonny Pinhorn. Distributed via Google Fonts.
|
||||
Full text: https://openfontlicense.org/ (OFL-1.1)
|
||||
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
|
||||
|
||||
WHY THIS FILE IS IN THIS REPO
|
||||
Poppins is DX-life's --font-family-base: the body face of every line of text on the pages the
|
||||
regions plugin clones. Their own site self-hosts it via @nuxt/fonts; we could not use theirs, so
|
||||
it is vendored here from Google Fonts under the OFL, which permits redistribution.
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
WHY IT WAS NEEDED — and why no CSS check caught it.
|
||||
A computed-style diff of our region dashboard against live doxa.life reported 68 of 68 properties
|
||||
IDENTICAL while our body text was rendering in a system fallback, because BOTH sides merely NAME
|
||||
"Poppins" first in font-family and a first-family comparison therefore matched. The difference only
|
||||
became visible against a glyph-width oracle: the same string at the same size measured 334.58px
|
||||
here against 317px on their page — 5.5% wider, i.e. a different face.
|
||||
document.fonts.check() is NOT a usable oracle for this: it returned true for families never
|
||||
vendored (Oswald, Brandon Grotesque) and false for Bebas Neue on DX-life's own page.
|
||||
See _qa-cycle/02-builder-cycle/_probe-glyph-width.mjs.
|
||||
|
||||
WHAT IS HERE, AND WHY ONLY THIS
|
||||
Weights 400/500/600/700 only — the four the regions surface actually asks for (measured across
|
||||
their _globals, _cards, _buttons and _filters SCSS: medium x10, regular x4, bold x2, plus the
|
||||
600 in the prayer-coverage legend). Subsets latin + latin-ext only. 44 KB in total, with Google's
|
||||
own unicode-ranges reproduced in the @font-face so the subset split behaves as it does on their
|
||||
site. Extra weights and scripts are deliberately NOT vendored — bloat is a repo rule here.
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
FILENAME: extensionless on purpose. check:public-hygiene refuses *.txt in a served folder and is
|
||||
right to, while the OFL requires this licence to accompany redistributed font files. Do not
|
||||
rename this to LICENSE.txt.
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
|
|
|||
21
data/vendor/tabulator/LICENSE
vendored
Normal file
21
data/vendor/tabulator/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2026 Oli Folkerd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
21
data/vendor/vue/LICENSE
vendored
Normal file
21
data/vendor/vue/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-present, Yuxi (Evan) You and Vue contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
44
documentation/THIRD-PARTY.md
Normal file
44
documentation/THIRD-PARTY.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Third-party content in the shipped tree
|
||||
|
||||
Every non-original thing `dx-maps/` ships, what licenses it, and whether the licence or
|
||||
attribution actually travels with it. Compiled 2026-09-28 by inspecting the SHIPPED tree, not the
|
||||
source lists -- the question is what a recipient receives.
|
||||
|
||||
Notes live HERE and not beside the assets: `check:public-hygiene` fails on "documentation in a
|
||||
served folder". The licence travels with the asset; the explanation travels with the repo.
|
||||
|
||||
| Item | Source | Licence | Does it travel? | Gap |
|
||||
|---|---|---|---|---|
|
||||
| Fonts: bebas-neue, figtree, noto-serif-devanagari, oswald, poppins | Google Fonts / foundry repos | OFL-1.1 | **Yes** — each `LICENSE` is the family's own upstream OFL, verbatim, with its real copyright line | none (closed 2026-09-28) |
|
||||
| `vendor/vue/vue.global.prod.js` | vuejs/core | MIT | **Partly** — the minified banner carries `(c) 2018-present Yuxi (Evan) You` and `@license MIT`, but NOT the permission notice MIT asks to include | closed: upstream `LICENSE` now vendored beside it |
|
||||
| `vendor/tabulator/tabulator.min.js` + `.min.css` | olifolkerd/tabulator v6.3.1 | MIT | **No** — the `.js` had only `(c) Oliver Folkerd 2025`; the `.css` had nothing | closed: upstream `LICENSE` now vendored beside it |
|
||||
| `boundaries/gb-*.geojson` (4 files) | geoBoundaries gbOpen (www.geoboundaries.org) | CC BY — the India ADM1 file records **CC BY 2.5 IN**, sourced from DataMeet/Election Commission of India | **Partly** — provenance sits in ONE sidecar, `gb-IND-ADM1-full.meta.json` (source, boundaryID, licence, fetch URL, processing). The other boundary files have none, and no human-readable attribution ships | **OPEN** — CC BY requires attribution to accompany the work |
|
||||
| `boundaries/world-adm0*.geojson` | not verified — probably Natural Earth | Natural Earth is public domain | n/a if Natural Earth | **OPEN** — provenance unconfirmed for this file |
|
||||
| `people-groups/*` (3 files, 8 MB) | peoplegroups.org | **not established** | no terms ship | **OPEN, judgment** — redistribution terms unknown |
|
||||
| `pplr/*` (6 files, 16 MB) | not resolved from `plugin-designs/pplr-data-maps/scripts/build-data.mjs` | **not established** | nothing ships | **OPEN, judgment** — owner should name the upstream |
|
||||
| `tables/*` (304 files, 36 MB) | derived by `library/scripts/research/fetch-*.mjs` from several upstream APIs | **varies, not established per table** | nothing ships; there is no source or licence doc in `tables/` | **OPEN, judgment** — the largest gap by volume |
|
||||
| `images/worldmap.svg` | recorded in `images/PROVENANCE` | see that file | the PROVENANCE file ships and explains why no licence file sits beside it | none claimed; reasoning recorded rather than assumed |
|
||||
| `previews/*` (378 files) | our own poster previews | ours | n/a | none |
|
||||
| Natural Earth 1:10m populated places (`library/scripts/posters/data/ne-places.json`) | naturalearthdata.com | **public domain**, no attribution required | n/a | none. Their 1:10m ROADS were deliberately not shipped because of an ODbL attribution obligation |
|
||||
|
||||
## Provenance records
|
||||
|
||||
| Vendored file | Upstream URL | sha256 |
|
||||
|---|---|---|
|
||||
| `data/vendor/tabulator/LICENSE` | `https://raw.githubusercontent.com/olifolkerd/tabulator/master/LICENSE` | `191a2ee554684e1064c897b432f0e1bc6dfa714ca045d3f6ea2cf692cbd398b7` |
|
||||
| `data/vendor/vue/LICENSE` | `https://raw.githubusercontent.com/vuejs/core/main/LICENSE` | `49190d1f742dd61e47fe816df4963e5186a2a62100d9122f9883a60ea32973ac` |
|
||||
| `data/fonts/bebas-neue/LICENSE` | `https://raw.githubusercontent.com/google/fonts/main/ofl/bebasneue/OFL.txt` | see `documentation/vendored-fonts.md` |
|
||||
| `data/fonts/poppins/LICENSE` | `https://raw.githubusercontent.com/google/fonts/main/ofl/poppins/OFL.txt` | see `documentation/vendored-fonts.md` |
|
||||
|
||||
## What is NOT closed, and why it is not mine to close
|
||||
|
||||
Four rows above are marked OPEN. Two are simply unverified and someone should look
|
||||
(`world-adm0` provenance, the `pplr` upstream). Two need a decision rather than a fetch:
|
||||
**people-groups** and the 304 **tables** carry data whose redistribution terms are not established,
|
||||
and 36 MB of derived tables is the largest exposure by volume. Attaching a licence I have not
|
||||
confirmed would be worse than the present gap, so those go to the Driver.
|
||||
|
||||
The geoBoundaries row is closable by work rather than judgment: CC BY needs attribution to
|
||||
accompany the data, and the pattern already exists in this repo -- the `.meta.json` sidecar. Extending
|
||||
it to the other boundary files plus one shipped attribution line would close it.
|
||||
|
||||
94
documentation/vendored-fonts.md
Normal file
94
documentation/vendored-fonts.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Vendored fonts — licences and why each one is here
|
||||
|
||||
The fonts ship in `data/fonts/<family>/`, which `bun run tree` stages into the served tree at
|
||||
`dx-maps/assets/fonts/`. Each family's `LICENSE` is its OWN upstream OFL text, verbatim, carrying
|
||||
that family's real copyright line. Nothing in this file is part of any licence.
|
||||
|
||||
★ THESE NOTES LIVE HERE, NOT BESIDE THE FONTS, AND THAT IS THE POINT. check:public-hygiene fails on
|
||||
`documentation in a served folder`: a NOTES.md next to each LICENSE is prose a reader of the drop-in
|
||||
tree should not be served. The old bebas-neue LICENSE said so in its own text -- "prose does not
|
||||
belong in a public served folder" -- and I put three NOTES.md files there anyway before the gate
|
||||
caught it. The licence travels with the font; the explanation travels with the repo.
|
||||
|
||||
## bebas-neue
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Licence source | `https://raw.githubusercontent.com/google/fonts/main/ofl/bebasneue/OFL.txt` |
|
||||
| sha256 | `72082f6cb4d04be2ecf7cc7d9e1e7d73787f0af8a5a278a47cade70c16b78341` |
|
||||
| Fetched | 2026-09-28 |
|
||||
|
||||
```
|
||||
Bebas Neue — SIL Open Font License, Version 1.1
|
||||
Designer: Ryoichi Tsunekawa (Dharma Type). Distributed via Google Fonts.
|
||||
Full text: https://openfontlicense.org/ (OFL-1.1)
|
||||
WHY THIS FILE IS IN THIS REPO
|
||||
DX-life's /regions pages set --font-family-heading to 'Bebas Neue'. Their own site does not
|
||||
ship the file either — @nuxt/fonts downloads it at build and self-hosts it, which is why their
|
||||
computed font-family reads "Bebas Neue", "Bebas Neue Fallback: ...". We cannot @font-face from
|
||||
their origin: their font file is served without CORS headers, so a cross-origin face silently
|
||||
fails and every heading fell back to a condensed system stack. That substitution was the ONLY
|
||||
remaining difference between our regions dashboard and theirs (7 computed properties, all of
|
||||
them this one font and its knock-on sizes).
|
||||
So the font is vendored here, from Google Fonts, under the OFL that permits redistribution —
|
||||
self-hosted, never a CDN, the same rule public/assets/vendor/tabulator/ follows.
|
||||
Subsets and unicode-ranges are Google's own (css2 API, v16), reproduced verbatim in the
|
||||
@font-face declared by library/composables/useDocumentFont.js.
|
||||
NOTE ON THIS FILENAME — it is deliberately extensionless.
|
||||
check:public-hygiene refuses *.md / *.markdown / *.txt anywhere in the served tree ("no docs in a
|
||||
served folder", product rule 2026-09-16), and it is right to: prose does not belong in a public
|
||||
mirror. But the OFL requires this licence to accompany the font files wherever they are
|
||||
redistributed, and the font files DO ship — they are fetched by the @font-face at runtime. So the
|
||||
licence travels with them under the conventional extensionless name, which satisfies the rule
|
||||
without weakening it. Do not rename this to LICENSE.txt.
|
||||
```
|
||||
|
||||
## poppins
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Licence source | `https://raw.githubusercontent.com/google/fonts/main/ofl/poppins/OFL.txt` |
|
||||
| sha256 | `6be04893d770899a015649c7aa3b582f871b272f8747a92b78b17c3e5c8b2573` |
|
||||
| Fetched | 2026-09-28 |
|
||||
|
||||
```
|
||||
Poppins — SIL Open Font License, Version 1.1
|
||||
Designers: Indian Type Foundry, Jonny Pinhorn. Distributed via Google Fonts.
|
||||
Full text: https://openfontlicense.org/ (OFL-1.1)
|
||||
WHY THIS FILE IS IN THIS REPO
|
||||
Poppins is DX-life's --font-family-base: the body face of every line of text on the pages the
|
||||
regions plugin clones. Their own site self-hosts it via @nuxt/fonts; we could not use theirs, so
|
||||
it is vendored here from Google Fonts under the OFL, which permits redistribution.
|
||||
WHY IT WAS NEEDED — and why no CSS check caught it.
|
||||
A computed-style diff of our region dashboard against live doxa.life reported 68 of 68 properties
|
||||
IDENTICAL while our body text was rendering in a system fallback, because BOTH sides merely NAME
|
||||
"Poppins" first in font-family and a first-family comparison therefore matched. The difference only
|
||||
became visible against a glyph-width oracle: the same string at the same size measured 334.58px
|
||||
here against 317px on their page — 5.5% wider, i.e. a different face.
|
||||
document.fonts.check() is NOT a usable oracle for this: it returned true for families never
|
||||
vendored (Oswald, Brandon Grotesque) and false for Bebas Neue on DX-life's own page.
|
||||
See the builder-cycle probes: _probe-glyph-width.mjs.
|
||||
WHAT IS HERE, AND WHY ONLY THIS
|
||||
Weights 400/500/600/700 only — the four the regions surface actually asks for (measured across
|
||||
their _globals, _cards, _buttons and _filters SCSS: medium x10, regular x4, bold x2, plus the
|
||||
600 in the prayer-coverage legend). Subsets latin + latin-ext only. 44 KB in total, with Google's
|
||||
own unicode-ranges reproduced in the @font-face so the subset split behaves as it does on their
|
||||
site. Extra weights and scripts are deliberately NOT vendored — bloat is a repo rule here.
|
||||
FILENAME: extensionless on purpose. check:public-hygiene refuses *.txt in a served folder and is
|
||||
right to, while the OFL requires this licence to accompany redistributed font files. Do not
|
||||
rename this to LICENSE.txt.
|
||||
```
|
||||
|
||||
## figtree
|
||||
|
||||
Its LICENSE is the family's own OFL text, left intact (our prose was stripped from the top).
|
||||
|
||||
```
|
||||
Figtree — SIL Open Font License, Version 1.1
|
||||
Designer: Erik D. Kennedy. Distributed via Google Fonts.
|
||||
Vendored as the free stand-in for Brandon Grotesque (commercial), by product decision
|
||||
2026-09-24. Chosen by MEASUREMENT, not by name: at 500/19.5312px Figtree renders
|
||||
'FULL PROFILE' at 126px against Brandon's 128px on live doxa.life — the closest of 19
|
||||
libre grotesques tested. See _probe-glyph-width.mjs.
|
||||
```
|
||||
|
||||
|
|
@ -1637,6 +1637,12 @@ function main() {
|
|||
// vendor — DataTable/TabulatorTable inject Tabulator from assetsBase at runtime.
|
||||
'vendor/tabulator/tabulator.min.js',
|
||||
'vendor/tabulator/tabulator.min.css',
|
||||
// ★ AND THEIR LICENCES, because this list is what decides whether a notice TRAVELS. Both are MIT,
|
||||
// which requires the permission notice to be included with the software. Tabulator's minified .js
|
||||
// carried only a copyright line and its .css carried nothing; Vue's banner names MIT but omits the
|
||||
// permission text. Shipping the code without the notice is the gap, and an allowlist that omits
|
||||
// the LICENSE guarantees it stays open however carefully the file is vendored.
|
||||
'vendor/tabulator/LICENSE',
|
||||
// vendor/vue — the GALLERY SHELL's own Vue (generate-gallery.mjs loadVue()), not a bundle's.
|
||||
//
|
||||
// ★ ADDED 2026-09-22, and it had never been staged. generate-gallery.mjs:542 requests
|
||||
|
|
@ -1652,6 +1658,7 @@ function main() {
|
|||
// claim was false. A tree dropped on a foreign host or opened over file:// is exactly the
|
||||
// case the self-hosted rung exists for.
|
||||
'vendor/vue/vue.global.prod.js',
|
||||
'vendor/vue/LICENSE',
|
||||
]) {
|
||||
const from = join(pubAssets, rel)
|
||||
if (!existsSync(from)) { console.warn(`[generate-tree] WARN missing tree asset: assets/${rel}`); continue }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"props": {
|
||||
"profile": "board",
|
||||
"view": "province",
|
||||
"panel": "map",
|
||||
"urlState": "query"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -85,6 +85,16 @@ const back = M.buildQuery({ view: 'zero', filterSet: { band: ['yes', 'partial'],
|
|||
ok(back.includes('lang=ar') && back.includes('list=700') && back.includes('band=yes%7Cpartial') && back.includes('view=zero'),
|
||||
`query builds and keeps lang/list: ${back}`)
|
||||
ok(M.parseQuery('?view=nonsense').view === 'board', 'an unknown view falls back to board')
|
||||
// the tree is the default panel: a bare URL names no panel, only the map is written, both are read back
|
||||
ok(M.parseQuery('').panel === '' && M.parseQuery('?panel=map').panel === 'map' && M.parseQuery('?panel=tree').panel === 'tree'
|
||||
&& M.buildQuery({ panel: 'map' }) === 'panel=map' && M.buildQuery({ panel: 'tree' }) === '', 'the tree is the default panel; ?panel=map restores the map')
|
||||
// an OLD map link (map state, no ?panel=) still opens the map; a tree link with map state says panel=tree
|
||||
ok(['?group=abai-sungai', '?groups=abai-sungai,hadrami-arabs', '?country=IND', '?province=IN-BR', '?view=province'].every((s) => M.parseQuery(s).panel === 'map')
|
||||
&& M.parseQuery('?access=none&band=yes').panel === '' && M.parseQuery('?codes=abf').panel === '' && M.parseQuery('?country=IND&panel=tree').panel === 'tree',
|
||||
'old links with map state and no panel open the map; a bare or map-free link opens the tree')
|
||||
const treeBack = M.buildQuery({ panel: 'tree', keep: '?country=IND', filters: false })
|
||||
ok(M.parseQuery('?' + treeBack).panel === 'tree' && M.parseQuery('?' + M.buildQuery({ panel: 'tree', group: 'abai-sungai' })).panel === 'tree',
|
||||
'a tree view with map state round-trips to the tree')
|
||||
// embed-contract parity with Gospel Resources: ?groups= (peoples by slug) and ?codes= / ?code= (languages)
|
||||
const pg = M.parseQuery('?groups=abai-sungai,hadrami-arabs&codes=abf,ayh')
|
||||
ok(pg.groups.length === 2 && Array.isArray(pg.filters.language) && pg.filters.language.join('|') === 'abf|ayh', 'parses ?groups= (slugs) and ?codes= (languages), comma-separated as Gospel Resources writes them')
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ try {
|
|||
const res = T('gospel-internet-resources.json')
|
||||
const open = rows.find((r) => r.access_detail === 'none' && r.province) || rows[0]
|
||||
|
||||
async function render(lang) {
|
||||
async function render(lang, extra = { legend: 'open' }) {
|
||||
const app = createSSRApp({
|
||||
setup() {
|
||||
const b = useBoard()
|
||||
|
|
@ -75,7 +75,7 @@ try {
|
|||
const appI18n = i18n.createAppI18n()
|
||||
app.use(appI18n)
|
||||
appI18n.global.locale.value = lang
|
||||
app.provide('profileConfig', ref({ profile: 'board', lang }))
|
||||
app.provide('profileConfig', ref({ profile: 'board', lang, ...extra })) // legends start collapsed; the content checks open them
|
||||
const errors = []
|
||||
app.config.errorHandler = (e) => { errors.push(String(e && e.stack || e)) }
|
||||
app.config.warnHandler = (m) => { if (/Unhandled error|Property .* was accessed during render but is not defined/.test(m)) errors.push(m) }
|
||||
|
|
@ -84,6 +84,11 @@ try {
|
|||
}
|
||||
|
||||
const en = await render('en')
|
||||
// the Driver's default (2026-09-28): with no knob both legends start collapsed behind their "Legend" button
|
||||
const shut = await render('en', {})
|
||||
ok(!/data-tree-legend|class="gim-legend"/.test(shut.html) && /class="gtr-min"/.test(shut.html) && /class="gim-legend-min"/.test(shut.html),
|
||||
'en: with no knob, the tree and map legends start collapsed')
|
||||
ok(/data-tree-credit[^>]*>[\s\S]*?Glottolog[\s\S]*?CC BY 4\.0/.test(shut.html), 'en: the Glottolog CC BY credit is on screen with the legend collapsed')
|
||||
ok(en.errors.length === 0, `en: every panel rendered with no error (${en.errors.length}${en.errors[0] ? ': ' + en.errors[0].slice(0, 160) : ''})`)
|
||||
for (const [name, re] of [['GiFacetRail', /data-facet="access"/], ['GiGrid', /data-grid-total/], ['GiCharts', /data-region="charts"/],
|
||||
['GiTree', /data-tree-legend[\s\S]*data-tree-poster/], ['GiResources', /data-resources-total/], ['GiMap', /data-region="map"/]]) ok(re.test(en.html), `en: ${name} rendered its region`)
|
||||
|
|
|
|||
|
|
@ -103,9 +103,10 @@ useShadowStyles(
|
|||
'.gic2{grid-area:combos;display:flex;align-items:center;gap:10px;padding:6px 14px;background:#fff;border-bottom:1px solid #d1d4cd;' +
|
||||
'min-width:0;font:12.5px/1.3 Roboto,-apple-system,"Segoe UI",sans-serif;color:#3b463d}' +
|
||||
'.gic2-h{flex:none;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:#6d766e}' +
|
||||
// The selection side is BOUNDED and clips: its nowrap placeholder ran under "Try a combination" at 1280
|
||||
// and 1440 (rega r82). It now ellipsizes; the full text stays in the title tooltip.
|
||||
'.gic2-sel{display:flex;align-items:center;gap:6px;min-width:0;flex:0 1 auto;max-width:40%;overflow:hidden}' +
|
||||
// The selection side is BOUNDED at 40% (rega r82: the placeholder ran under "Try a combination"), but only
|
||||
// its chips and placeholder clip: they scroll or ellipsize. No overflow here and no min-width:0, so the
|
||||
// flex:none label sets the minimum width and is never cut ('YOUR S…' at 390, the zh/ru/hi/ar labels at 1280).
|
||||
'.gic2-sel{display:flex;align-items:center;gap:6px;flex:0 1 auto;max-width:40%}' +
|
||||
'.gic2-none{color:#9aa19b;font-style:italic;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}' +
|
||||
'.gic2-try{display:flex;align-items:center;gap:6px;min-width:0;flex:1 1 auto;overflow-x:auto;scrollbar-width:thin}' +
|
||||
'.gic2-c{flex:none;display:inline-flex;align-items:center;gap:6px;border:1px solid #d1d4cd;background:#f3f3f1;color:#3b463d;border-radius:999px;' +
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ const ready = ref(false)
|
|||
// Province shading: 'zero' = groups with nothing found in their own language (from the rows in scope),
|
||||
// 'women' / 'men' = NFHS-5 ever used the internet (India only; China has no verified provincial source).
|
||||
const provinceMode = ref('zero')
|
||||
const legendOpen = ref(true)
|
||||
// the Driver: legends start collapsed, one tap on "Legend" opens them; the embed knob legend:"open" opens them
|
||||
const legendOpen = ref(board.profileConfig.value?.legend === 'open')
|
||||
let unmounted = false
|
||||
let adm1Table = null // { [shapeName]: { women, men } } India, NFHS-5
|
||||
const nfhsAttribution = ref('')
|
||||
|
|
@ -313,8 +314,6 @@ onMounted(async () => {
|
|||
// resize the canvas too, or it stretches the old bitmap.
|
||||
let ro = null
|
||||
onMounted(() => {
|
||||
// A phone opens on the map, not on its legend: at 390px the card covered most of the map (rega r75).
|
||||
if (mapEl.value && mapEl.value.clientWidth && mapEl.value.clientWidth < 520) legendOpen.value = false
|
||||
if (mapEl.value && typeof ResizeObserver !== 'undefined') {
|
||||
ro = new ResizeObserver(() => { try { mapRef.value && mapRef.value.resize() } catch (_) {} })
|
||||
ro.observe(mapEl.value)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ const mapRef = shallowRef(null)
|
|||
const err = ref('')
|
||||
const ready = ref(false)
|
||||
const hoverId = ref('')
|
||||
const legendOpen = ref(true)
|
||||
// the Driver: legends start collapsed, one tap on "Legend" opens them; the embed knob legend:"open" opens them
|
||||
const legendOpen = ref(board.profileConfig.value?.legend === 'open')
|
||||
let unmounted = false
|
||||
let ro = null
|
||||
|
||||
|
|
@ -242,7 +243,6 @@ async function build() {
|
|||
} catch (_) {}
|
||||
}
|
||||
onMounted(() => {
|
||||
if (el.value && el.value.clientWidth && el.value.clientWidth < 520) legendOpen.value = false // a phone opens on the tree
|
||||
if (el.value && typeof ResizeObserver !== 'undefined') {
|
||||
// the engines only follow WINDOW resizes; a dragged rail or an un-hidden panel must resize the canvas
|
||||
ro = new ResizeObserver(() => { try { mapRef.value && mapRef.value.resize() } catch (_) {} })
|
||||
|
|
@ -328,6 +328,10 @@ useShadowStyles(
|
|||
'.gtr-row .gtr-dot{margin-top:3px}' +
|
||||
'.gtr-levels{margin:6px 0 2px;font-size:11.5px}' +
|
||||
'.gtr-note{font-size:10.5px;color:#6d766e;margin-top:4px}' +
|
||||
// ALWAYS ON, like a map's attribution control: the CC BY credit never hides behind the collapsed legend
|
||||
'.gtr-credit{position:absolute;z-index:3;inset-block-end:0;inset-inline-end:0;padding:2px 6px;font:11px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;' +
|
||||
'background:rgba(255,255,255,.82);color:#3a4048;border-start-start-radius:3px}' +
|
||||
'.gtr-credit a{color:#2a5d8f;text-decoration:none}' +
|
||||
'.gtr-poster{display:flex;align-items:center;gap:6px;margin-top:6px;padding-top:6px;border-top:1px solid #e6e8e3;font-weight:600}' +
|
||||
'.gtr-poster select,.gtr-poster button{font:inherit;font-weight:400;font-size:12px;border:1px solid #d1d4cd;background:#fff;color:#3b463d;border-radius:6px;min-height:26px;padding:2px 8px;cursor:pointer}' +
|
||||
'.gtr-poster button:focus-visible,.gtr-poster select:focus-visible{outline:2px solid #92b195;outline-offset:1px}' +
|
||||
|
|
@ -385,5 +389,7 @@ useShadowStyles(
|
|||
<div class="gtr-note">{{ T('gospelInternet.tree.sources', 'Tree: Glottolog (CC BY 4.0). Resources: eBible, Faith Comes By Hearing, Scripture Earth, PNG.Bible, JESUS Film, Global Recordings and others, keyed by ISO 639-3.') }} {{ citation }}</div>
|
||||
</div>
|
||||
<button v-else type="button" class="gtr-min" @click="legendOpen = true">{{ T('gospelInternet.legend.show', 'Legend') }}</button>
|
||||
<!-- names and a licence, the same in every language (like the map's own attribution): not catalogue text -->
|
||||
<div class="gtr-credit" data-tree-credit><a href="https://glottolog.org" target="_blank" rel="noopener noreferrer">Glottolog</a> · <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener noreferrer">CC BY 4.0</a></div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,12 @@ export const VIEWS = ['board', 'zero', 'province', 'platforms']
|
|||
* The published names are kept so links already shared keep restoring. */
|
||||
export const SHARE_FACETS = Object.fromEntries(Object.entries(URL_KEYS))
|
||||
|
||||
// MAP STATE: what a link written while the map was the default carries and only the map shows (an open or
|
||||
// selected people group's pin, a country or province, the province view). Such a link with no ?panel= opens
|
||||
// the MAP, so old map links keep working; a bare link opens the tree (dx-shepherd ENDTOKEN-GOINT-ATTR).
|
||||
const MAP_STATE = ['group', 'groups', 'country', 'province']
|
||||
export const hasMapState = (q) => MAP_STATE.some((k) => q.get(k)) || q.get('view') === 'province'
|
||||
|
||||
/** Parse `?view=&access=…&group=` into { view, filters, group }. Unknown views fall back to board. */
|
||||
export function parseQuery(search) {
|
||||
const q = new URLSearchParams(search || '')
|
||||
|
|
@ -104,7 +110,8 @@ export function parseQuery(search) {
|
|||
const codes = [...list('codes'), ...list('code')].filter((c) => /^[a-z]{3}$/.test(c))
|
||||
if (codes.length && !filters.language) filters.language = codes.length === 1 ? codes[0] : codes
|
||||
const groups = list('groups').filter((s) => /^[a-z0-9-]+$/.test(s))
|
||||
const panel = q.get('panel') === 'tree' ? 'tree' : 'map'
|
||||
// '' = not in the URL: the board opens on its default (the language tree, the Driver 2026-09-28)
|
||||
const panel = ['tree', 'map'].includes(q.get('panel')) ? q.get('panel') : hasMapState(q) ? 'map' : ''
|
||||
const tree = /^[a-z0-9-]{3,40}$/.test(q.get('tree') || '') ? q.get('tree') : ''
|
||||
// A tree node narrows by LANGUAGE (its ISO codes); the URL carries the node, not the code list.
|
||||
if (tree) delete filters.language
|
||||
|
|
@ -118,7 +125,6 @@ export function buildQuery({ view, filterSet, group, keep, panel, tree, filters
|
|||
// delete nor write them, so the two writers never fight over one address bar
|
||||
for (const name of [...(filters ? Object.keys(URL_KEYS) : []), 'view', 'group', 'panel', 'tree']) q.delete(name)
|
||||
if (view && view !== 'board') q.set('view', view)
|
||||
if (panel === 'tree') q.set('panel', 'tree')
|
||||
if (tree) q.set('tree', tree)
|
||||
if (filters) for (const [name, key] of Object.entries(URL_KEYS)) {
|
||||
if (tree && key === 'language') continue
|
||||
|
|
@ -127,6 +133,8 @@ export function buildQuery({ view, filterSet, group, keep, panel, tree, filters
|
|||
q.set(name, Array.isArray(v) ? v.join('|') : String(v))
|
||||
}
|
||||
if (group) q.set('group', group)
|
||||
// the tree is the default, so it is written only where map state would otherwise open the map
|
||||
if (panel === 'map' || (panel === 'tree' && hasMapState(q))) q.set('panel', panel)
|
||||
return q.toString()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export function useBoard() {
|
|||
const sort = ref({ key: 'population', dir: -1 })
|
||||
const openSlug = ref('') // the group in the detail drawer ('' = closed)
|
||||
// ── the centre panel: the geographic map or the language tree (loaded on first use) ───────────────
|
||||
const centre = ref('map')
|
||||
const centre = ref('tree') // the Driver: people come for the language tree; the map is one tap away
|
||||
const tree = shallowRef(null) // indexTree(doc) + { meta }
|
||||
const resources = shallowRef(null) // { meta, rows } — gospel links for the board's languages
|
||||
const treeNode = ref('') // the tree node the board is narrowed to ('' = none)
|
||||
|
|
@ -180,7 +180,8 @@ export function useBoard() {
|
|||
if (codes) bus.setFilter('language', codes)
|
||||
const group = q.group || cfg().group || ''
|
||||
if (group) pendingGroup = group
|
||||
const panel = q.panel === 'tree' || cfg().panel === 'tree' ? 'tree' : 'map'
|
||||
// the URL wins, then the embed knob (profile-config panel: 'map'), then the default: the tree
|
||||
const panel = q.panel || (cfg().panel === 'map' ? 'map' : 'tree')
|
||||
if (q.tree) pendingTree = q.tree
|
||||
if (q.groups && q.groups.length) pendingSelection = q.groups
|
||||
setCentre(panel)
|
||||
|
|
|
|||
|
|
@ -604,7 +604,7 @@
|
|||
"bundle": "gospel-internet",
|
||||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"output": "dx-maps/gospel-internet/gospel-internet.js",
|
||||
"integrity": "sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y",
|
||||
"integrity": "sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn",
|
||||
"elements": [
|
||||
"gospel-internet-board"
|
||||
],
|
||||
|
|
@ -623,7 +623,7 @@
|
|||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-internet/board/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -662,6 +662,7 @@
|
|||
"props": {
|
||||
"profile": "board",
|
||||
"view": "province",
|
||||
"panel": "map",
|
||||
"urlState": "query"
|
||||
},
|
||||
"element": "gospel-internet-board",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
(function () {
|
||||
var slot = document.getElementById('slot');
|
||||
var status = document.getElementById('status');
|
||||
var BASE_CONFIG = {"assetsBase":"../../../assets","profile":"board","view":"province","urlState":"query"};
|
||||
var BASE_CONFIG = {"assetsBase":"../../../assets","profile":"board","view":"province","panel":"map","urlState":"query"};
|
||||
// MapLibre map pages AND shell pages (app-shell / nested-mfe-spa) need NO token:
|
||||
// the ladder below is skipped entirely and the element mounts immediately.
|
||||
// (A leaf whose engine is MapLibre at RUNTIME joins them — see PROVIDER below.)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -604,7 +604,7 @@
|
|||
"bundle": "gospel-internet",
|
||||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"output": "dx-maps/gospel-internet/gospel-internet.js",
|
||||
"integrity": "sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y",
|
||||
"integrity": "sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn",
|
||||
"elements": [
|
||||
"gospel-internet-board"
|
||||
],
|
||||
|
|
@ -623,7 +623,7 @@
|
|||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-internet/board/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-1QKATO2zc5eFQVqHLIMkyHx2RcXSpvb2U/+1i2wyQFAy9WQPCR449qwFFu1+8S/Y\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-qdNXX4aWhgMvTym3O4xUzPisrdct3miCaawTtbiRDztj/iJyMddyGv7yA7mPtAzn\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -662,6 +662,7 @@
|
|||
"props": {
|
||||
"profile": "board",
|
||||
"view": "province",
|
||||
"panel": "map",
|
||||
"urlState": "query"
|
||||
},
|
||||
"element": "gospel-internet-board",
|
||||
|
|
|
|||
Loading…
Reference in a new issue