Commit graph

21 commits

Author SHA1 Message Date
Doxa Maps
06a48a9b42 plugin-designs: neutral comments in shared/ownerless maps
Rewrite code comments across doxa-research-map, upg-neighbor,
research-map-plus-grid, rudder-voice, people-groups-grid, big-picture,
prayer-walk-planner and all-people-groups to drop internal role names
from measurement and provenance notes, keeping each technical fact.
Comments only; behaviour unchanged.
2026-10-01 00:22:13 +00:00
Doxa Maps
f544ca6334 Print on phones, everywhere: the map's poster dialog, research-map, the gospel tree, the planner and the wall
Every other caller of the in-place print moves onto the phone path the poster wall already uses:
printImageBlob (the map's poster/download dialog and research-map) prints the page itself on a phone,
and the gospel tree, the prayer-walk planner and the wall of people groups call printDocument. A caller
without its own progress dialog gets a shared Print bar when the tap has expired, labelled through the
caller's own translator. Desktop keeps the in-place frame unchanged. Source only; the bundles are rebuilt
when it lands.
2026-09-30 07:34:19 +00:00
Doxa Maps
c2473d0e41 exp's maps keep the reason and drop the speaker: no comment quotes or cites the Driver
21 lines in seven of exp's bundles (upg-neighbor 4, prayer-walk-planner 8, doxa-simple-map 2, pplr-data-maps 2, wall-of-people-groups 1, doxa-vision-card 1, all-people-groups 3): each keeps its engineering reason, its dates and its measurements; the speaker and the quotes go (a quote becomes a plain statement of the defect). Comments only. Five bundles rebuild byte-identical; doxa-simple-map and pplr-data-maps change only in their Vue scoped-style ids, which hash the SFC source.
2026-09-29 08:56:44 +00:00
Doxa Maps
7b5de8bcfa Prayer Walk Planner: on a phone the zone list opens from a ☰ Zones button instead of being hidden
On a narrow screen the list used to be display:none, so a walker could not see, select, rename or delete a zone there. It is now a drawer over the map from the reading-end edge (right in English, left in Arabic), behind a ☰ Zones (n) button that leads the toolbar; a tap outside, × or Escape closes it and focus returns to the button. On a wide screen it is the sidebar as before, its border now on the reading side. Two words in the planner's own 11-locale strings: zones, closeZones.
2026-09-29 02:58:03 +00:00
Doxa Maps
807c78cc64 Every Experimental map carries its own hosted ?, where its controls already are
Map controls: prayer-walk-planner (under fullscreen), gospel-internet (the map and the language
tree, the board's default view), country-posters (the country map, its entry profile) and
all-people-groups get the library HelpButton with hosted. Own bars, where there is no control
column: the Wall (after the Bless/Social toggle), the PPLR hub (the end of its view bar) and the
vision card (its top corner, at the end of the reading direction so Arabic mirrors it) use
useHostedHelp. The vision card's label is the library's own "About this map" in each of the
eleven locales, copied into vision.json rather than translated again. UPG Neighbor is left as it
is: its bar already has an About button that opens the full explainer.

Each "?" stays hidden until a host marks its pane, so nothing changes until the app answers.
2026-09-29 02:35:35 +00:00
Doxa Maps
786a834610 Map errors in every language, and print buttons that show they are busy (UPG Neighbor, All People Groups, Prayer Walk Planner, Wall) 2026-09-28 18:34:16 +00:00
Doxa Maps
4dbb21203e Prayer Walk Planner: the phone toolbar fits, with the list actions behind More
At 390px the toolbar was one sideways-scrolling row with no scroll cue, so Delete
selected, Select all, Clear selection and the count ran past the edge. On a phone
Draw, Print, More and the count now fit one row, and More opens the three list
actions as a row beneath; desktop is unchanged. "More" in all eleven languages.
2026-09-28 00:36:26 +00:00
Doxa Maps
54d04fc388 Prayer Walk Planner: the resolved instance id is exposed under its own name
The element declares an instanceId PROP and also exposed an instanceId member,
so Vue warned "Exposed property 'instanceId' already exists on custom element".
The exposed member is the RESOLVED id (the prop, or a generated one), so it is now
resolvedInstanceId. The window.PrayerWalkPlanner registry keeps keying on it,
unchanged, and no reader used the exposed name.
2026-09-27 04:38:17 +00:00
doxa-maps-88655c70
b862c7d61a Export gate reads the tree name from the build config
After the dx-maps rename, its public/doxa-maps literal made it SKIP (exit 2) without measuring.
Validated against its baseline on the renamed tree: 4/4 one-click exports, 1700x2200 PNGs,
0 errors, as before the rename. Also: usage examples and present-tense comments in the
gallery and planner name /dx-maps/ or the configured tree; both bundles rebuild byte-identical.
2026-09-25 21:32:35 +00:00
doxa-maps-88655c70
e94cb56b79 Map labels ask for a font the engine's glyph server has: one shared helper
library/maps/label-font.js picks a symbol layer's text-font by engine: Noto Sans
(Regular, or Bold for medium/bold) under MapLibre, which is what OpenFreeMap
serves, and exactly the caller's fonts under Mapbox. Mapbox maps render as
before.

useMapClustering hard-coded 'DIN Offc Pro Medium, Arial Unicode MS Bold', which
OpenFreeMap does not have. Under MapLibre every native cluster count requested
it and got a 404; MapLibre then drew the counts in its fallback weight instead
of the intended bold. It now uses the helper, as do upg-neighbor (replacing its
own copy), prayer-walk-planner, and key-cities' city-name labels, which named no
font and so asked for MapLibre's default (also a 404 on OpenFreeMap).

Measured by importing the real composable in a browser, 3,000 points in native
mode:
- main under MapLibre: 1 glyph 404 (DIN Offc Pro Medium,Arial Unicode MS Bold),
  and the counts drawn in the fallback weight;
- this change under MapLibre: 0 glyph 404s, text-font Noto Sans Bold, 22
  clusters with counts in bold;
- this change under Mapbox: text-font still exactly DIN Offc Pro Medium /
  Arial Unicode MS Bold.
No production map reaches native cluster counts today (Simple Research and
Country Posters force the 'mst' line mode), so this was latent in shared code.

Correction to b1e0c7fa: it said those labels "drew nothing". The screenshots here
show MapLibre falls back and draws them. The defect was the failed glyph request
on every load and the wrong weight, not missing text.

Rebuilt: country-posters, doxa-research-map, upg-neighbor, prayer-walk-planner,
key-cities.
2026-09-25 13:48:10 +00:00
doxa-maps-88655c70
ff36d0dfcc Merge remote-tracking branch 'origin/main' into exp-shepherd/shelf-gate
# Conflicts:
#	package.json
#	public/doxa-maps/doxa-maps-spa/manifest.json
#	public/doxa-maps/index.html
#	public/doxa-maps/manifest.json
#	public/doxa-maps/prayer-walk-planner/prayer-walk-planner.js
#	public/doxa-maps/prayer-walk-planner/prayer-walk-planner/index.html
2026-09-25 11:36:00 +00:00
doxa-maps-88655c70
b1e0c7fa61 check:experimental-shelf: every experimental plugin, measured working, and two label fonts that 404'd
The Driver asked for "all experimental plugins working"; this makes it a
measurement. The gate derives the shelf from the Research Gallery's own rules
(manifest minus curated tabs and NOT_EXPERIMENTAL), then checks every bundle on
its leaf page and on its Gallery tab, under mapbox and maplibre. It requires
content inside the plugin's own element, 0 page errors, 0 failed requests, no
loading text, and for each map 'load' fired AND its own GeoJSON data drawn. An
empty source does not count. Maps are found by wrapping the engine's Map class
in an init script, so no bundle changes for it. It serves this checkout on an
OS-assigned port, and self-tests first: a deliberately broken bundle must fail,
or it exits 3.

Prayer Walk Planner's data is the user's own (zones in localStorage), so the
gate seeds one zone and requires it drawn, printing the seed and reason on every
run rather than exempting the bundle.

It found two real defects on the way. Both are fixed here, and both failed the
gate before the fix:
- UPG Neighbor: every label layer asked MapLibre's default fonts (or Mapbox's
  DIN) from OpenFreeMap, which 404s, so no label drew under maplibre. labelFont()
  now picks Noto Sans under MapLibre; Mapbox is unchanged.
- Prayer Walk Planner (always MapLibre): zone titles never drew, for the same
  reason. The label layer now names Noto Sans Regular.

Result: 28/28 PASS across all 7 shelf bundles.
2026-09-25 11:35:18 +00:00
doxa-maps-88655c70
30e6e5699b Prayer Walk Planner: full screen from any embed, and a print proven sharp by resolution
The Driver asked for full-screen, and for "a print that stays sharp when
zoomed".

Full screen. The planner mounts the shared FullscreenButton (the library's
Fullscreen-API control, with its CSS pseudo-fullscreen fallback for iOS
Safari) as a map overlay. It full-screens the whole planner, toolbar and
map, not just the canvas. Measured from a 520px embed in the Research
Gallery on a foreign page: the planner took 1280x800 and the map grew 459
→ 739px. The label is translated (Arabic "تبديل ملء الشاشة"), because the
planner now keeps the shared vue-i18n instance on its own locale. Before,
Share and Full screen took the PAGE language even when the gallery sent
another.

Print resolution, measured, not asserted. The printed map lays out at
7.53 x 8.4 in on the letter sheet (printable width = 8.5in minus the two
12mm margins; emulateMedia alone does not apply @page margins, which
misread it once as 8.48in). Its image is 2400x2688 px, so 319 x 320 DPI.
A 1:1 crop of the zoomed print was read: small street names (Sancaktar
Yokuşu Sokağı, Baki Dede Sokağı), individual building outlines and
footpaths are crisp, because the map is rendered from vector tiles at
pixelRatio 2, not upscaled from the screen. One PDF page.

check:prayer-walk-planner gains both: full screen from a 520px embed must
take the viewport, and the printed map must measure >= 300 DPI. 10/10
PASS.
2026-09-25 10:51:21 +00:00
doxa-maps-88655c70
bad1ae9d7e Prayer Walk Planner: one scrolling toolbar row on a phone, Draw and Print first
On a 390px phone the toolbar wrapped to three rows, 139px of an 844px
screen above the map a walker is trying to read. Below 640px it is now one
row that scrolls sideways: no label is truncated, and the two actions a
walk needs, Draw and Print, lead the row so they are always on screen.
2026-09-25 10:11:19 +00:00
doxa-maps-88655c70
319064a11b Map place names in the reader's language, on both engines
Every map translated its own chrome but left the basemap's country, city
and street names in the style default (English on Mapbox, Latin + native
on OpenFreeMap) in all eleven locales. A reader in Arabic saw an Arabic
legend over an English map.

library/maps/localize-labels.js uses each engine's supported mechanism:
- Mapbox: map.setLanguage(lang). It also localizes the Standard style's
  IMPORTED basemap, which a layer rewrite cannot reach.
- MapLibre: basemap symbol layers point at
  ['coalesce', name:<lang>, name:latin, name]. English restores each
  layer's original expression, re-captured on every style load so a theme
  switch never restores the previous style's labels.
Only layers whose text-field reads an OSM name field are touched. A
plugin's own label layers (zone titles, group names) are never rewritten.

useMapInstance applies it on every style.load and when the reader's
language changes (ProfileLoader's 'lang', else profile-config locale, else
?lang / <html lang>). The Prayer Walk Planner drops its private copy for
the shared one.

Measured on the Prayer Progress map, ar/zh/ru/hi:
- Mapbox getLanguage() == locale in all four.
- MapLibre: 13/14 symbol layers localized, and the plugin's own label
  layer untouched.
Read in the Arabic screenshots: القاهرة, الإسكندرية, السويس, الأردن on
both engines. check:gallery-embed (+ :maplibre) and
check:prayer-walk-planner pass. The shared change reaches all 11 bundles
that compile initializeMap, and all are rebuilt here. check:shared-reached
marker basemap-labels-localized: 10/10 consumers.
2026-09-25 09:46:21 +00:00
doxa-maps-88655c70
4fb8176b00 Shape Arabic, Hebrew and Persian map labels on MapLibre maps too
library/maps/rtl-text.js installed the RTL text plugin for MAPBOX only
(window.mapboxgl, from api.mapbox.com). So every MapLibre map, i.e. every
keyless embed, drew RTL place names as disconnected, reversed glyphs.
Measured on the Prayer Walk Planner's printed Arabic sheet.

Under it was a second gap. Bundle entries call the installer at LOAD
time, before useMapInstance has lazily injected a MapLibre engine, so even
an engine-aware installer found nothing to install into.

- rtl-text.js is engine-aware. Mapbox is unchanged (same URL, same lazy
  call). MapLibre gets the same plugin package from jsDelivr, keyless and
  not api.mapbox.com, so keyless pages still make zero Mapbox requests.
  New ensureRtlTextPlugin(gl) waits until it is LOADED, for callers that
  capture a single frame (print).
- useMapInstance installs it the moment an engine global exists, the one
  point every map passes through.
- The planner drops its private copy for the shared helper.

Measured: a print map of downtown Cairo (MapLibre, keyless) shows shaped
Arabic street and place names (شارع التحرير, مسجد الطباخ …); plugin
status "loaded", fetched from jsDelivr. check:gallery-embed passes js,
iframe, neighbour and linked (14 tabs); :maplibre passes;
check:prayer-walk-planner passes.

The shared change reaches all 11 bundles that compile initializeMap, and
all are rebuilt here. check:shared-reached gains an rtl-text-maplibre
marker (10/10 consumers carry it; cities-internet, gospel-resources and
my-upg-100-list boot maps another way and are not affected).

Also: the new Experimental bundle pplr-data-maps loads the Mapbox geocoder
under maplibre (pplrData.js:38). It is an owned, printed MAPBOX_KNOWN entry
routed to its owner; delete it in their fix commit.
2026-09-25 08:20:12 +00:00
doxa-maps-88655c70
b78e7d1b52 Prayer Walk Planner: fill the page, and print a real walking map
The Driver: "the prayer walk planner is still a very tiny map, not
expanding the full page", and the printed map was a ~10px sliver.

Tiny map. Measured: the leaf filled (1003x739), but inside the Research
Gallery the map was 747x76 on desktop and 390x0 on a phone. The planner
mounts its SFC as the custom-element root (that is what puts its client
API on the element). So Vue's attribute fallthrough copied the gallery's
inline host style onto the root .pwp, and that inline display:block beat
.pwp{display:flex}. inheritAttrs:false keeps host attributes on the host.
The map now fills at 1280x800 and 390x844, in the leaf and in the gallery.
The gallery gets the same guard: its .vtl had survived only through
display:grid !important.

Sliver print. Print used to snapshot the LIVE map, so the page got that
map's on-screen size and shape. Each zone now renders in its own
temporary MapLibre map:
- page-shaped (1200x1344 at pixelRatio 2, a 2400x2688 image, ~320 dpi on
  the 7.5x8.4in frame)
- keyless OpenFreeMap liberty, with streets and building footprints
- fitted to the zone, with the zone outlined on top
- destroyed once its pixels are taken, and the reader's map never moves
One zone = one letter page, with the map filling it.

Localized. The planner read no language at all. It now follows
language → profile-config locale → ?lang → <html lang>, in all 11
locales (private src/strings.js): toolbar, list, status, default zone
names, and the printed sheet (lang/dir set, so Arabic prints RTL).
Street and place labels prefer the OSM name:<lang>. Two Arabic defects
were found by reading the printed sheet, and fixed:
- coordinates were bidi-scrambled → isolated LTR
- map labels were unshaped glyphs → the RTL text plugin is installed
  for MapLibre, from jsDelivr, and awaited before the print frame
  (library/maps/rtl-text.js only does it for Mapbox)

renderZoneMap(id) joins the client API: the print PNG without the dialog.

New gate check:prayer-walk-planner:
- 4 size cells (leaf/gallery × desktop/phone)
- a real zone in Balat, Istanbul renders a >=2000px non-blank print map
- a Spanish toolbar
- 0 page errors
Both gates now prove the served tree is this checkout's (manifest equals
disk) before measuring, and exit 75 NOT MEASURED otherwise.
check:gallery-embed's Big Picture link now asserts that the grid shows
exactly the facet's own count.
2026-09-25 07:21:42 +00:00
doxa-maps-88655c70
e5fe33b230 prayer-walk-planner can be shared — the last published map with no sharing path
check:share-policy's known-debt line is now GONE: it named two published maps, then
one, now zero. Every published map has exactly one decided sharing path.

★ THE OPEN QUESTION WAS ANSWERED BY THE CODE, NOT BY A RULING. This was held back
asking "is it MEANT to be shareable? a link carrying no route arrives as an empty
planner, and a button that reliably produces a blank map is worse than none". That
framing assumed a design choice existed. It did not: the profile keeps polygons in
localStorage and has NO BACKEND — its own header says so — so a URL cannot carry a
drawn route under any design. The shareable artefact is the PLANNER, exactly as
every other map shares its own page. The failure only exists if the link CLAIMS to
carry the route, so the status strip says once, on click: "This link shares the
planner. Your drawn zones stay on this device." — using the status mechanism the
profile already had rather than adding a surface.

★ ADDING <ShareButton> WAS NOT ENOUGH, AND check:share-policy COULD NOT SEE IT.
The button rendered NOWHERE. This bundle mounts its SFC directly as the custom
element — deliberate, it is what surfaces the defineExpose()d client-side API on the
DOM element ("the only API we can build has to be CLIENT SIDE") — and so installed
no i18n. ShareButton calls useI18n(), which throws in setup(), and index.js's own
header records the cost: "a thrown setup() removes the whole shadow-DOM subtree
silently." Measured: a runtime SyntaxError, five toolbar buttons rendered, the sixth
absent — while the policy gate, which counts <ShareButton> in SOURCE, said 1 button
and PASSED. That blind spot is now written into the gate, pointing at the smoke that
does catch it.

Fixed WITHOUT changing the mounting shape: defineCustomElement takes configureApp
whichever component is the root, so i18n installs and the API stays on the element.
The direct-mount decision and i18n were orthogonal; I had assumed they were coupled.

VERIFIED AT THE RENDERED LAYER: share-roundtrip-smoke resolves the url 200 text/html
and REOPENS it as <prayer-walk-planner> with a rendered canvas. The client-side API
was re-verified after the change — all 7 exposed methods present, addPolygon ->
listPolygons round-trips, 0 page errors. i18n verified in 6 locales: Share /
Compartir / مشاركة / साझा करें / 分享 / Partager. Added to smoke:share-all, now
12/12 pass 0 fail.
2026-09-24 08:23:30 +00:00
doxa-maps-88655c70
390b90b343 Prayer-walk POSTERS: per-zone walking-scale street map on a poster sheet
Some checks are pending
build-gate / build (push) Waiting to run
The runtime sibling of the UPG-100 poster generator (that one is
build-time over 28 fixed zones; walk zones are drawn live). For each
selected zone the camera fits the zone bounds (maxZoom 17), waits for
idle, and snapshots the keyless MapLibre canvas INSIDE a render frame
-- triggerRepaint + once('render'), because MapLibre does not preserve
the WebGL buffer and toDataURL outside a frame returns a blank white
image that still counts as a 'valid' data URL (measured: the first
sheet shipped a white rectangle; pixel check now reads avg 212
brightness, 0% blank). Camera restored afterwards.

The sheet borrows the poster design language: dark-green title band,
kicker with the zone centre, framed street map with the zone polygon
painted on it, a prayer charge line, zone corners, and the honest
credit (Map (c) OpenFreeMap, Data (c) OpenStreetMap contributors) --
letter portrait, printed in place via the shared
@map/composables/printInPlace.js. Agent flow re-verified end-to-end:
addPolygon -> selectPolygon -> print() raises the dialog once.

(Also restores the public API wrappers a block edit clobbered --
caught by the page's own ReferenceError before commit.)
2026-09-24 01:06:51 +00:00
doxa-maps-88655c70
04a55c66b2 Prayer Walk Planner: full-page dev mount + agent-triggerable in-place print
Some checks are pending
build-gate / build (push) Waiting to run
Two halves of the Driver's directive:

1. FULL PAGE (BUG-PRAYER-WALK-PLANNER-MAP-DOES-NOT-FILL-THE-PAGE).
   The BUILT leaf was already full-viewport; the DEV staging page boxed
   the element in a centred 640px card -- measured 781x584 at y=206 on
   a 1440x900 viewport, exactly 'the map is not filling the full
   page'. The dev harness now mounts fixed inset:0 like the leaf
   (two-host rule).

2. AGENT-OPERABLE PRINT. PrayerWalkPlanner.print() used window.open,
   which is pop-up-blocked without a user gesture -- an agent calling
   the documented API got silence. The poster-wall's in-place
   hidden-iframe mechanism (e957cbe) graduates VERBATIM to
   @map/composables/printInPlace.js (library bar: 2+ consumers,
   parameterized) and the planner uses it; @page pins letter portrait
   (the 839b0c4 ruling).

Verified end-to-end as an agent would: registry -> addPolygon ->
selectPolygon -> print() => {opened:true,count:1}, hidden iframe
print() fired once, portrait pinned, zero pop-ups.
2026-09-24 00:58:10 +00:00
doxa-maps-88655c70
79b12baee9 Add prayer-walk-planner: experimental polygon prayer-walking map
Some checks are pending
build-gate / build (push) Waiting to run
Draw/delete/batch-select polygons on a keyless MapLibre+OpenFreeMap basemap,
print a per-zone prayer-walking plan (title + coordinates) via an in-browser
print dialog, everything persisted to localStorage only — no backend, per
the JAMstack constraint. Root SFC mounts directly as the custom element
(no ProfileLoader — needs no token/dataSource/profile-switching), so
defineExpose() bridges addPolygon/deletePolygon/selectPolygon/print/list
straight onto the DOM element for agent-driven use; window.PrayerWalkPlanner
mirrors the same API for discovery. Researched keyless satellite imagery
(Esri World Imagery, NASA GIBS/MODIS, USGS orthophotos): none qualified —
Esri restricts free use to OSM tracing only, GIBS/MODIS tops out at 250m/px
(useless for a neighborhood walk), USGS is US-only — so this ships the
keyless vector basemap already proven in library/styles/registry.js.
2026-09-23 16:22:56 +00:00