Keep the reason, drop the speaker, in the UPG-100 and Country Posters plugins
The 40 baselined references in my-upg-100-list (32) and country-posters (8),
plus 3 unbaselined ones in data/*.js, each wrapped an engineering reason in
an attribution. The reason stays; the speaker, the quotation marks and the
pronouns go. Dates and measurements are kept where they were ("decided
2026-09-24", "Reported 2026-09-24: the UPG-100 list legend on the map
overflowed beyond the height of the map"). Where a quote carried a
requirement, the requirement is now stated directly.
Comment-only, one line for one line (73 against 73), including the
meta.json note, which does not reach the manifest. Both bundles build
byte-identical to the base. Route test: 0 files under library/ or
internal/.
This commit is contained in:
parent
4ea988a4c9
commit
538e2fb782
8 changed files with 73 additions and 73 deletions
|
|
@ -369,7 +369,7 @@ const profileConfig = inject('profileConfig')
|
|||
|
||||
// ── SEARCH BAR IS MAPBOX-ONLY, AND ITS ABSENCE MUST NOT LEAVE A HOLE ─────────
|
||||
// Same contract as doxa-research-map (the reference implementation), deliberately copied rather
|
||||
// than re-invented. Driver 2026-09-24: the geocoder is DEFERRED to Mapbox, because
|
||||
// than re-invented. Decided 2026-09-24: the geocoder is DEFERRED to Mapbox, because
|
||||
// GeocoderComponent's maplibre path still calls makeMapboxForwardGeocode(accessToken) — the search
|
||||
// REQUEST goes to Mapbox whichever engine draws the tiles, so a keyless map with a search bar is
|
||||
// not keyless and would bill the very key the maplibre option exists to avoid.
|
||||
|
|
@ -382,7 +382,7 @@ const hasGeocoder = computed(() => {
|
|||
})
|
||||
// SemanticTreeLegend's --stl-top defaults to 60px, and that 60px exists ONLY to reserve room for a
|
||||
// search bar above it. With no bar the legend must RISE into the vacated space instead of leaving a
|
||||
// dead band — the Driver's explicit requirement. 14px matches the reference.
|
||||
// dead band — a requirement, not a preference. 14px matches the reference.
|
||||
// How far the legend may rise depends on WHAT ELSE is in the strip the search bar vacated.
|
||||
// LegendTools sits at top:14px on the tabs its predicate allows, so raising the legend to 14 there
|
||||
// prints the toolbar across the legend's header — MEASURED on country-posters. Ask the toolbar's own
|
||||
|
|
@ -1236,8 +1236,8 @@ async function openPacket(variant = 'full') {
|
|||
if (_packetBusy) return
|
||||
_packetBusy = true
|
||||
try {
|
||||
// ★ THE PACKET PRINTS, AND IT PRINTS THROUGH THE SHARED RENDERER (Driver-lens audit card 2, and
|
||||
// the Driver's own rule: an export button opens the print dialog, never hands over a file). This
|
||||
// ★ THE PACKET PRINTS, AND IT PRINTS THROUGH THE SHARED RENDERER (product-lens audit card 2, and
|
||||
// the product rule: an export button opens the print dialog, never hands over a file). This
|
||||
// built a jsPDF document and downloaded packet.pdf while its poster sibling, two functions up,
|
||||
// already printed. The fix is NOT to wrap that PDF in a dialog — a PDF blob cannot go in the
|
||||
// <img> the in-place print frame uses, and every workaround (embed, object, the browser's own
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"country-posters/country-poster-wall",
|
||||
"country-posters/countries-map"
|
||||
],
|
||||
"note": "The Driver's 2-tab Country Posters element, structured like my-upg-100-list/upg-100-mfe: a SELECT VIEW switcher (the SHARED library/components/ViewSelect.vue — this is its THIRD consumer, which is why it was extracted rather than copied a third time) over Country Posters at [0,0] and Map at [1,0]. LIST FIRST, MAP SECOND per the Driver's principle that maps are a secondary view. The wall already dispatches 'doxa-country-posters:show-on-map' (country-poster-wall.vue:78); until now only the app shell listened, so the action did nothing in a dropped embed — this shell owns it and swaps to the map tab. pinHint:false: the cloned panes carry no coach-mark, and the composite must declare it because a pane's config comes from here, not from the child's own meta. One ShareButton in the header, sharing the COMPOSITE leaf, per the recorded share policy.",
|
||||
"note": "The 2-tab Country Posters element, structured like my-upg-100-list/upg-100-mfe: a SELECT VIEW switcher (the SHARED library/components/ViewSelect.vue — this is its THIRD consumer, which is why it was extracted rather than copied a third time) over Country Posters at [0,0] and Map at [1,0]. LIST FIRST, MAP SECOND, on the principle that maps are a secondary view. The wall already dispatches 'doxa-country-posters:show-on-map' (country-poster-wall.vue:78); until now only the app shell listened, so the action did nothing in a dropped embed — this shell owns it and swaps to the map tab. pinHint:false: the cloned panes carry no coach-mark, and the composite must declare it because a pane's config comes from here, not from the child's own meta. One ShareButton in the header, sharing the COMPOSITE leaf, per the recorded share policy.",
|
||||
"config": {
|
||||
"pinHint": false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
* poster wall behind a single view switcher, structured like the UPG-100 list. The switcher is
|
||||
* the library's shared ViewSelect, not a third hand-rolled copy.
|
||||
*
|
||||
* Driver: "Country Posters is supposed to have two tabs, structured like the
|
||||
* UPG-100 list."
|
||||
* Country Posters has two tabs, structured like the
|
||||
* UPG-100 list.
|
||||
*
|
||||
* ★ THE CONTROL IS SHARED, NOT COPIED. That is the whole point of the card: this
|
||||
* is the THIRD place the SELECT VIEW switcher is needed, and it uses the library's
|
||||
|
|
@ -25,9 +25,9 @@
|
|||
* • one ShareButton in the header, per the recorded share policy
|
||||
* (check:share-policy) — the MAP pane must not grow a second one
|
||||
*
|
||||
* LIST FIRST, MAP SECOND. The Driver's principle, already applied in upg-100-mfe:
|
||||
* "maps don't have to be primary. You can use lists and grids for the primary view
|
||||
* and maps as a secondary view… then they transition into the map." The poster wall
|
||||
* LIST FIRST, MAP SECOND. The principle already applied in upg-100-mfe: a map need
|
||||
* not be the primary view — a list or a grid can be, with the map secondary and
|
||||
* reached as a transition from it. The poster wall
|
||||
* is the concrete artefact a newcomer can act on; the map is where it is placed.
|
||||
*/
|
||||
import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue'
|
||||
|
|
@ -239,7 +239,7 @@ useShadowStyles(PANE_GRID_STYLES + `
|
|||
no such rule exists and every pane collapses to 0 — the "elements are not embedding"
|
||||
report. Declaring :host makes the element fill its slot in every deploy method. */
|
||||
:host{ display:block; width:100%; height:100%; min-height:420px; }
|
||||
/* ── palette: DX-life material light. NO AUTO-DARK (Driver 2026-09-20): a system set to
|
||||
/* ── palette: DX-life material light. NO AUTO-DARK (decided 2026-09-20): a system set to
|
||||
dark mode must not flip this element. ── */
|
||||
.cpmfe{
|
||||
--ui-bg:#ffffff; --ui-bg-elevated:#f3f3f1; --ui-bg-accented:#f3f3f1;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ export const REGION_KEY = {
|
|||
export function buildLegendNodes(clusters, opts = {}) {
|
||||
const deafCount = opts.deafCount ?? 123;
|
||||
const records = Array.isArray(opts.records) ? opts.records : null;
|
||||
// ★ OPTIONAL GENERATED TITLE (Driver, 2026-09-27). `opts.title(zone)` returns a localized title
|
||||
// ★ OPTIONAL GENERATED TITLE (2026-09-27). `opts.title(zone)` returns a localized title
|
||||
// composed from already-translated parts, or NULL for a zone whose title cannot be generated — see
|
||||
// data/zone-title.js. Passing nothing keeps every hand-written English label exactly as it was, which
|
||||
// is why this is an option rather than a rewrite: the legend, the poster and the packet adopt it
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
* Then `npm run check:zoning-declared -- --list <id>` grades the zoning, and the legend, poster wall and
|
||||
* packet all follow, because all three already enumerate through leafZones(buildLegendNodes(zones)).
|
||||
*
|
||||
* ZONING RULE, from the Driver (2026-09-27), recorded here because it is the judgement the format cannot
|
||||
* ZONING RULE (2026-09-27), recorded here because it is the judgement the format cannot
|
||||
* capture: cluster by GEOGRAPHY FIRST so the poster maps make sense, balanced with LANGUAGE — languages
|
||||
* usually cluster geographically, so the two mostly agree, and where they disagree geography wins for a
|
||||
* map's sake. UPG-100's own 28 already mix the two: the country_codes zones are geographic, and the 12
|
||||
|
|
@ -38,8 +38,8 @@
|
|||
*
|
||||
* ⚠ THE DEAF ZONE IS THE KNOWN EXCEPTION AND MUST NOT BE FORCED. `deaf` is affinity bloc A017: 123 groups
|
||||
* spanning every region, global by construction, and the one poster whose zone cannot be a map extent. It
|
||||
* breaks the geography-first rule the rest of the pattern rests on, and the Driver has said he has not
|
||||
* solved it. A list whose clusters include an affinity or language bloc inherits the same open question —
|
||||
* breaks the geography-first rule the rest of the pattern rests on, and it is recorded as not yet
|
||||
* solved. A list whose clusters include an affinity or language bloc inherits the same open question —
|
||||
* declare it in bloc_zones so the gate treats it as a second partition, and do NOT invent a geography for
|
||||
* it to make a validator happy.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@
|
|||
* cardStocks — the reader picks the prayer-card size at print time.
|
||||
* posterAction="print" — open the print dialog instead of saving a file.
|
||||
* hidePrayerListWhenSoon — no "coming soon" placeholder on a wall whose list DOES print.
|
||||
* The last two are a Driver ruling, not a preference (2026-09-23): "three print buttons, each
|
||||
* opening an in-browser print dialog, no silent file download as the default action. A raw
|
||||
* 'Download' button and a 'Prayer List: coming soon' placeholder were exactly the two buttons he
|
||||
* objected to."
|
||||
* The last two are a product ruling, not a preference (2026-09-23): three print buttons, each
|
||||
* opening an in-browser print dialog, and no silent file download as the default action. A raw
|
||||
* 'Download' button and a 'Prayer List: coming soon' placeholder were exactly the two buttons
|
||||
* ruled out.
|
||||
*
|
||||
* CROSS-PANE COMMS — unchanged and still the decoupling that matters: this wall NEVER imports the
|
||||
* map. The shared component dispatches the window CustomEvent named below, scoped by instanceId,
|
||||
|
|
@ -210,7 +210,7 @@ const zoneSheet = makeZoneSheet({ allRecords: () => recordsFor.all(), assetsBase
|
|||
// catalogued — I told regions-plugin exactly that, and it was FALSE: this file's three keys are
|
||||
// present in all ELEVEN locales, 0 gaps (enumerated 2026-09-27; the only three, all string literals).
|
||||
// ⚠ IT GUARDS A BARE HOST, NOT A MISSING KEY. Those are different failures and I had them confused.
|
||||
// Product call taken by dx-shepherd (UPGHK, 2026-09-27) under the Driver's standing goal that the
|
||||
// Product call taken by dx-shepherd (UPGHK, 2026-09-27) under the standing goal that the
|
||||
// production maps be localized.
|
||||
const t = useOptionalT('')
|
||||
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ function testHandleKey() {
|
|||
// the ENGINES table in a profile is how two copies of one fact drift apart.
|
||||
const upgProvider = resolveMapProvider(profileConfig)
|
||||
const isMaplibre = upgProvider === 'maplibre'
|
||||
// The geocoder is deferred to Mapbox by decision (Driver 2026-09-24): GeocoderComponent's maplibre
|
||||
// The geocoder is deferred to Mapbox by decision (2026-09-24): GeocoderComponent's maplibre
|
||||
// path still calls makeMapboxForwardGeocode(accessToken), so the SEARCH REQUEST goes to Mapbox
|
||||
// whichever engine draws the tiles — a keyless map with a search bar is not keyless.
|
||||
const hasGeocoder = computed(() => {
|
||||
|
|
@ -349,7 +349,7 @@ const geocoderRef = ref(null) // <GeocoderComponent> exposed ref
|
|||
// catalog entry, so the labels below are what render. If these tabs are ever translated, add
|
||||
// `upg100Region`/`upg100Zone`/`upg100Group` to tabs.json rather than reusing a generic key.
|
||||
// ★ "Zone" RENAMED, ORDER DELIBERATELY UNCHANGED (2026-09-23).
|
||||
// Driver: "the user does not know what zones means" — the thing the map paints IS
|
||||
// A reader does not know what "zones" means — the thing the map paints IS
|
||||
// a UPG-100 list (27 of them); "zone" was our internal word leaking into the UI.
|
||||
// So the LABEL now names what it shows. Region stays as what it actually is: a way
|
||||
// to SORT those lists by world area when you want that.
|
||||
|
|
@ -383,7 +383,7 @@ const LEGEND_TABS = computed(() => [
|
|||
// MEASURED 2026-09-25 by expanding every caret on the Region tab — 37 rows, 9 at indent 0 and
|
||||
// 28 at indent 1, deepest level 1. There is no level 2, so tab index 2 resolved to an empty set
|
||||
// and the panel rendered its "No data at this level" empty state with the tab ACTIVE.
|
||||
// The Driver said "it doesn't need to have a People Group tab inside the legend"; the reason is
|
||||
// The legend does not need a People Group tab; the reason is
|
||||
// that clicking it showed nothing at all.
|
||||
// ⚠ IF PEOPLE-GROUP ROWS ARE EVER WANTED BACK, a third tab is not the mechanism — the tree would
|
||||
// need a third level (list -> group). Re-adding the tab without deepening the data reproduces
|
||||
|
|
@ -442,8 +442,8 @@ function legendOpts(records) {
|
|||
// the caller owns localizing it. MEASURED 2026-09-25 on the live Chinese render: the legend
|
||||
// header read "My UPG 100 List — Legend" in full English inside an otherwise localized panel,
|
||||
// beside translated tabs (区域 · UPG-100 名单) and a translated column header (人口).
|
||||
// The Driver's gift policy is explicit — "BEAUTIFUL: no clipped text, no placeholders, no English
|
||||
// leaking" — and an English title on the mobile showcase is exactly that.
|
||||
// The gift policy is explicit — beautiful: no clipped text, no placeholders, no English
|
||||
// leaking — and an English title on the mobile showcase is exactly that.
|
||||
// ★ THE HOUSE STYLE FOR A PROPER NOUN INSIDE A TRANSLATED PHRASE ALREADY EXISTS in this catalog:
|
||||
// "Doxa Regions" renders as "Doxa 区域" / "مناطق Doxa" / "Регионы Doxa" — the identifier stays
|
||||
// Latin, the frame translates. "UPG 100" is treated the same way rather than transliterated, so
|
||||
|
|
@ -500,8 +500,8 @@ useShadowStyles(
|
|||
// matches research-map's .rm-legend-*-slot (767px) so the whole map stack
|
||||
// flips at the same width instead of drifting per profile.
|
||||
// ★ The search bar above the legend was REMOVED from this profile, so the legend rises into the
|
||||
// vacated space (Driver 2026-09-23: "the search bar is removed... the legend needs to be moved
|
||||
// up so that there's not a gap where the search bar used to be"). --stl-top is the shared
|
||||
// vacated space (2026-09-23: with the search bar removed, the legend moves
|
||||
// up so that no gap is left where the search bar used to be). --stl-top is the shared
|
||||
// SemanticTreeLegend's offset-from-map-top; 8px matches its own left inset. Other profiles keep
|
||||
// the 60px default because they still have their search bar.
|
||||
'.upg-legend-desktop-slot{--stl-top:8px}' +
|
||||
|
|
@ -626,12 +626,12 @@ useShadowStyles(
|
|||
'max-width:380px!important;z-index:1200!important}' +
|
||||
'.mapboxgl-ctrl-top-left .mapboxgl-ctrl{margin:0!important;float:none!important}' +
|
||||
// Push the legend panel BELOW the geocoder pill (legend is top-left here too) —
|
||||
// ★ BUT ONLY IF THE PILL ACTUALLY RENDERED (Driver 2026-09-23, reported repeatedly: "the search
|
||||
// bar is removed, so the legend needs to move up — there's a gap where the search bar used to
|
||||
// be"). MEASURED in both hosts with a real token: the standalone leaf mounts the geocoder
|
||||
// ★ BUT ONLY IF THE PILL ACTUALLY RENDERED (2026-09-23, reported repeatedly: with the search
|
||||
// bar removed, the legend must move up — no gap where the search bar used to
|
||||
// be). MEASURED in both hosts with a real token: the standalone leaf mounts the geocoder
|
||||
// (top 12, h 35) and the legend belongs at 58; the cap-5 shell mounts NO geocoder at all, and
|
||||
// an unconditional 58px stranded the legend below a pill that was not there — the dead gap the
|
||||
// Driver kept reporting. One host cannot be fixed by breaking the other, so the offset is
|
||||
// reports kept naming. One host cannot be fixed by breaking the other, so the offset is
|
||||
// conditional on the pill EXISTING: default raised to 8px (matching .stl-panel's own left
|
||||
// inset), pushed down only when a rendered geocoder is actually in the tree.
|
||||
// ★ SET --stl-top, DO NOT SET top DIRECTLY. The library caps the panel at
|
||||
|
|
@ -641,8 +641,8 @@ useShadowStyles(
|
|||
// MEASURED 2026-09-24, upg-100-list hosted in a 420px-tall frame at 390px wide: top resolved to
|
||||
// 58px while max-height resolved to calc(100% - 20px), so the panel ran to 458px in a 420px map
|
||||
// and HUNG 38px PAST THE BOTTOM OF THE MAP — exactly 58 - 20. Same 38px at a 560px frame, because
|
||||
// the error is the offset mismatch, not the map's size. Driver 2026-09-24: "the upg 100 list
|
||||
// legend on the map is overflowing beyond the height of the map".
|
||||
// the error is the offset mismatch, not the map's size. Reported 2026-09-24: the UPG-100 list
|
||||
// legend on the map overflowed beyond the height of the map.
|
||||
// ⚠ WHY THIS IS INVISIBLE ON EVERY PUBLISHED LEAF: a standalone leaf gives the map the FULL
|
||||
// viewport, and a panel that overflows a full-height map is still inside the screen. It only
|
||||
// shows when a host makes the map SHORTER than the viewport, which is how the element is really
|
||||
|
|
@ -670,7 +670,7 @@ useShadowStyles(
|
|||
// measurement — correctly. rega will read the rows on Simple Research and Prayer Progress, which
|
||||
// have NO override, after landing 5. If they read RTL there, DELETE the two rules below too.
|
||||
// ── the original finding, kept because it explains the shape ────────────────
|
||||
// Driver-reported via repo, 2026-09-26, from the README's Arabic picture: under ?lang=ar the legend
|
||||
// Reported via repo, 2026-09-26, from the README's Arabic picture: under ?lang=ar the legend
|
||||
// panel stayed on the LEFT and the map controls sat on top of its top-left corner.
|
||||
//
|
||||
// ★ THE CAUSE IS ONE COMPONENT BEING RTL-AWARE AND ITS NEIGHBOUR NOT. MapToolbar.vue:42 already uses
|
||||
|
|
@ -1315,15 +1315,15 @@ provide('onSemanticTreeSelect', onLegendSelect)
|
|||
// prop because <LegendMobile> renders several levels down inside the shell, which is also
|
||||
// why onSemanticTreeSelect above is provided. Without this the mobile sheet rendered its
|
||||
// SemanticTreeLegend with exportEnabled defaulting to FALSE, so a phone reader could browse
|
||||
// the lists and had no way to print a packet or a poster — the half of this profile the
|
||||
// Driver called "the only way that map would be usable".
|
||||
// the lists and had no way to print a packet or a poster — the half of this profile without
|
||||
// which the map is not usable.
|
||||
provide('onLegendExport', onLegendExport)
|
||||
provide('legendExportCards', true) // the card deck prints for the selected list
|
||||
|
||||
// ── LEGEND EXPORT (top-right of the legend titlebar) ────────────────────────
|
||||
// The shared legend emits 'export' with 'poster' | 'packet' for the currently
|
||||
// SELECTED node — the Driver's ask: "I select one and it gives me print options at
|
||||
// the top right of the legend... that's the only way that map would be usable."
|
||||
// SELECTED node — the requirement: select one and its print options appear at
|
||||
// the top right of the legend; without that the map is not usable.
|
||||
//
|
||||
// Poster serves the SAME pre-rendered artefact the poster wall downloads — one
|
||||
// file, so the legend, the wall and the preview can never show three different
|
||||
|
|
@ -1333,14 +1333,14 @@ provide('legendExportCards', true) // the card deck prints for the selected li
|
|||
// One packet is ~3.5 MB (photos dominate); 28 zones would add ~100 MB to a repo
|
||||
// that ALREADY had a publish blocked at a 74 MB asset tree. Generating on click
|
||||
// costs zero repository bytes, and the photos come from URLs the records already
|
||||
// carry. This is the Driver's instruction: "instead of fetching the poster from
|
||||
// the public folder I want the client side code to generate the poster real time
|
||||
// when you click on the button so it does not take a lot of mb storage."
|
||||
// carry. The requirement: instead of fetching the poster from
|
||||
// the public folder, the client generates the poster in real time
|
||||
// at the click, so it does not cost megabytes of storage.
|
||||
//
|
||||
// The pages themselves come from library/packet/packet-render.js — the SAME module
|
||||
// the build-time generator uses, so the printed packet and the generated one cannot
|
||||
// drift. The card asked for exactly that: reuse, do not grow a fourth print path.
|
||||
// ★ PRINT STRAIGHT FROM THE MAP — the Driver's "that's the only way that map would be usable".
|
||||
// ★ PRINT STRAIGHT FROM THE MAP — without it the map is not usable.
|
||||
// Select a UPG-100 list in the legend and its print options appear at the legend's top right.
|
||||
//
|
||||
// ⚠ THE POSTER PATH USED TO HAND-ROLL AN <a download> OF THE .webp, which was wrong twice:
|
||||
|
|
@ -1526,7 +1526,7 @@ async function localizedZone(zone) {
|
|||
|
||||
// Every list across all three cluster buckets, flattened.
|
||||
//
|
||||
// ★ THE ONE SEAM THE `list` PROP WILL FEED. Driver 2026-09-26 via the shepherd: UPG-100 is the mass
|
||||
// ★ THE ONE SEAM THE `list` PROP WILL FEED. Decided 2026-09-26: UPG-100 is the mass
|
||||
// prayer mobiliser — a GENERATOR that takes a list in and puts posters, packets and cards out, so a
|
||||
// partner can generate their own. Every list-consuming path in this profile goes through THIS
|
||||
// function (allLists -> selectedLists, printPrayerPacket, the legend, the funnel), so when the
|
||||
|
|
@ -1627,7 +1627,7 @@ async function packetFunnel() {
|
|||
// A confident figure I cannot defend is exactly the defect class I have been removing from other
|
||||
// surfaces all day; this one I put here myself. funnelPage already falls back to the
|
||||
// provenance-only footnote when unassigned is absent, so the page simply stops claiming it.
|
||||
// Carded for the Driver: 27 geographic / 29 counting deaf, and whether "Central Asia + Pakistan"
|
||||
// Carded as a product question: 27 geographic / 29 counting deaf, and whether "Central Asia + Pakistan"
|
||||
// (55 groups, contiguous, poster-walled as a real region) is a prayer list rather than a residual.
|
||||
if (listId === LIST_DEFAULT) {
|
||||
// printableZones, not allLists: the count prints on page 1 ("one of N") and at the triangle's
|
||||
|
|
@ -1656,9 +1656,9 @@ async function packetFunnel() {
|
|||
}
|
||||
|
||||
// ── BATCH SELECTION MAKES A PACKET OR A POSTER ──────────────────────────────
|
||||
// Driver: the batch tool is "super buggy", and recolouring regions is not what is wanted —
|
||||
// "the batch selection tool should let you create your own poster or prayer packet based on
|
||||
// your own selection." So the selection is an INPUT to the two artefacts this app already
|
||||
// The batch tool was reported very buggy, and recolouring regions is not what is wanted —
|
||||
// the batch selection tool should let a reader create their own poster or prayer packet from
|
||||
// their own selection. So the selection is an INPUT to the two artefacts this app already
|
||||
// builds, and neither of these grows a third pipeline: both end in printPrayerPacket /
|
||||
// packetPrints.printPoster, the same calls the legend and the poster wall make.
|
||||
const batchBusy = ref(false);
|
||||
|
|
@ -1747,7 +1747,7 @@ async function selectionAsList() {
|
|||
throw keyedError('upgMap.nothingSelected', 'nothing is selected');
|
||||
}
|
||||
if (!API_BASE) throw keyedError('wall.hud.noApi', 'no people-groups service is configured');
|
||||
// ★ primary_language + imb_gsec FEED THE GENERATED PROFILE DESCRIPTION (Driver brief A1.5 col 2,
|
||||
// ★ primary_language + imb_gsec FEED THE GENERATED PROFILE DESCRIPTION (brief A1.5 col 2,
|
||||
// 2026-09-28). describeGroup builds a localized sentence — who, where, what language, how reached —
|
||||
// and GSEC is the only reachedness field that is a NUMBER: EvangelicalLevel is English prose from the
|
||||
// API ('2% or Greater but Less than 5%'), so interpolating it would leave every non-English packet
|
||||
|
|
@ -2322,7 +2322,7 @@ onMounted(async () => {
|
|||
// the shadow root v3.3.0, .mapboxgl-canvas-container position:STATIC where Mapbox declares
|
||||
// it absolute, .mapboxgl-canary display:block, and 0 markers. A canvas container with no
|
||||
// positioning context leaves every absolutely-positioned child — the pins — with nothing to
|
||||
// anchor to. Driver: "the polygons for the map and map colors and pins are not showing up".
|
||||
// anchor to. Reported: the map's polygons, colours and pins were not showing up.
|
||||
// The comment at the top of this file already says the engine loader is imported rather than
|
||||
// re-declared "because a second copy of one fact drifts"; the stylesheet URL was that second
|
||||
// copy, and it drifted.
|
||||
|
|
@ -2642,9 +2642,9 @@ onMounted(async () => {
|
|||
// nothing here. Sized for the count, not copied from the sibling: 6px at z=1 (+50%),
|
||||
// tapering back to the original 9px by z=8, where geography supplies the context and the
|
||||
// dots no longer need to carry it alone.
|
||||
// ★ REVERTED TOWARD THE SIBLINGS (Driver 2026-09-23: "the dots are way too big, I never
|
||||
// asked for them to be that big, they're larger than the dots on most of the other maps").
|
||||
// He is right and this was my overcorrection: an earlier pass raised z=1 from 4 to 6 to fix
|
||||
// ★ REVERTED TOWARD THE SIBLINGS (2026-09-23: the dots were far too big, larger than
|
||||
// the dots on most of the other maps, and nobody had asked for that size).
|
||||
// That report is right and this was my overcorrection: an earlier pass raised z=1 from 4 to 6 to fix
|
||||
// "hard to see", reasoning that 100 marks alone on a planet need more presence than
|
||||
// all-people-groups' 12,379. The reasoning was sound but the amount was not — at 6 this map
|
||||
// became the LARGEST of the set (all-people-groups is 2.2 at z=0, 3.4 at z=4), so it read as
|
||||
|
|
@ -2965,9 +2965,9 @@ onBeforeUnmount(() => {
|
|||
<LocationButton :map="mapRef" :is-dark="isDark" />
|
||||
<FullscreenButton :map-container="mapEl" :is-dark="isDark" />
|
||||
<ThemeToggleButton :is-dark="isDark" @toggle="handleToggleTheme" />
|
||||
<!-- ★ NO MAP-LEVEL SHARE BUTTON HERE (Driver 2026-09-23, reported twice): "there's a share
|
||||
button on the map, but there's ALSO a share button on the header of the website. That's
|
||||
two separate share buttons. We don't need the one on the map."
|
||||
<!-- ★ NO MAP-LEVEL SHARE BUTTON HERE (2026-09-23, reported twice): there was a share
|
||||
button on the map AND a share button in the header of the website — two separate
|
||||
share buttons, and the one on the map is not needed.
|
||||
The composite upg-100-mfe.vue:217 already mounts a ShareButton in the header, and that is
|
||||
the one to keep — it shares the whole cap-4 element, whereas this one shared only the map
|
||||
half, so the duplicate was also the less useful of the two.
|
||||
|
|
@ -3111,9 +3111,9 @@ onBeforeUnmount(() => {
|
|||
</span>
|
||||
</div>
|
||||
<div v-if="selCount" class="upg-hud-names">{{ selectedTerritories.map(t => t.name).join(', ') }}</div>
|
||||
<!-- ★ THE SELECTION MAKES SOMETHING. Driver: the batch tool is "super buggy", and letting
|
||||
you change the colour of regions is NOT what is wanted — "the batch selection tool
|
||||
should let you create your own poster or prayer packet based on your own selection."
|
||||
<!-- ★ THE SELECTION MAKES SOMETHING. The batch tool was reported very buggy, and letting
|
||||
a reader change the colour of regions is NOT what is wanted — the batch selection tool
|
||||
should let them create their own poster or prayer packet from their own selection.
|
||||
So the colour swatches and the hex input are GONE, and the actions that remain BUILD
|
||||
the two artefacts this app already knows how to make, from exactly what was picked.
|
||||
These reuse printPrayerPacket / printPoster rather than growing a third pipeline. -->
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ import ShareButton from '@map/components/map-controls/ShareButton.vue'
|
|||
// reader's locale and still ends at English.
|
||||
// The four keys this file uses (tabs.map / prayerResources / selectView / views) are present in all
|
||||
// ELEVEN locales, 0 gaps, enumerated 2026-09-27 — nothing here was relying on the fallback.
|
||||
// Product call taken by dx-shepherd (UPGHK, 2026-09-27) under the Driver's standing goal that the
|
||||
// Product call taken by dx-shepherd (UPGHK, 2026-09-27) under the standing goal that the
|
||||
// production maps be localized; it changes what seven vue-i18n-less bundles render, which is not a
|
||||
// decision a migration gets to make for itself.
|
||||
const t = useOptionalT('')
|
||||
|
|
@ -103,12 +103,12 @@ const spec = computed(() => ({
|
|||
instanceId: instanceId.value,
|
||||
...(cfg.value?.dataSource ? { dataSource: cfg.value.dataSource } : {}),
|
||||
},
|
||||
// RESOURCES FIRST, MAP SECOND (Driver 2026-09-23). "By default the first tab
|
||||
// shouldn't be the map. The first tab should be the prayer resources" — that pane
|
||||
// RESOURCES FIRST, MAP SECOND (2026-09-23). By default the first tab is not the
|
||||
// map; it is the prayer resources — that pane
|
||||
// already explains what a UPG-100 list IS at the top and shows the posters, so it
|
||||
// needs no explanation; a choropleth of zones does. Generalised by him as a design
|
||||
// principle: "maps don't have to be primary. You can use lists and grids for the
|
||||
// primary view and maps as a secondary view… then they transition into the map."
|
||||
// needs no explanation; a choropleth of zones does. Generalised as a design
|
||||
// principle: a map need not be primary. A list or a grid can be the
|
||||
// primary view, with the map secondary and reached as a transition from it.
|
||||
//
|
||||
// ORDER LIVES IN `at[0]`, NOT IN THIS ARRAY'S ORDER. buildPaneColumn SORTS by
|
||||
// at[0] (pane-wrapper.js), while ViewSelect renders this array VERBATIM. Swapping
|
||||
|
|
@ -193,15 +193,15 @@ function paneElement(pane) {
|
|||
// `lang` is the alias the page may carry; children read `locale`.
|
||||
if (inherited.locale === undefined && inherited.lang !== undefined) inherited.locale = inherited.lang
|
||||
const pcfg = { ...inherited, ...spec.value.config, ...pane.config }
|
||||
// ★ NO "TAP A COLOURED DOT" COACH-MARK ON THIS PROFILE, AT ALL (Driver, 2026-09-27).
|
||||
// ★ NO "TAP A COLOURED DOT" COACH-MARK ON THIS PROFILE, AT ALL (2026-09-27).
|
||||
// ProfileLoader mounts PinHint by default and self-gates it on a map <canvas> existing, which is a
|
||||
// sound gate for the question it asks ("is there a map here?") and the WRONG question for this
|
||||
// profile. UPG-100's map is a POSTER PREVIEW: the reader is looking at what will be printed, and there
|
||||
// is no per-dot detail to open. So the pill invites a gesture that does nothing, and on the phone the
|
||||
// Driver's screenshots show it over a page with no coloured dots on it at all.
|
||||
// reported screenshots show it over a page with no coloured dots on it at all.
|
||||
// ⚠ THIS SUPERSEDES THE inert WORK FOR THIS PROFILE, and that is worth saying plainly rather than
|
||||
// quietly leaving both in: PILLRISK was "the pill shows over the posters", and I fixed WHERE it shows.
|
||||
// The Driver's answer is that it should not show HERE, full stop — a narrower and more correct claim
|
||||
// The ruling is that it should not show HERE, full stop — a narrower and more correct claim
|
||||
// than mine. The inert signal stays because it is still right for its own reasons (a stacked pane
|
||||
// should not be in tab order or the a11y tree, and studio's PinHint change serves profiles that DO
|
||||
// want the pill), but it is no longer what keeps this pill off this app.
|
||||
|
|
@ -215,7 +215,7 @@ function paneElement(pane) {
|
|||
|
||||
// ★ TELL THE INVISIBLE PANE THAT IT IS INVISIBLE. `inert` on every pane except the active one.
|
||||
//
|
||||
// THE DEFECT, reported by the Driver and traced by repo (PILLRISK, 2026-09-27): PinHint's "Tap a
|
||||
// THE DEFECT, reported from use and traced by repo (PILLRISK, 2026-09-27): PinHint's "Tap a
|
||||
// coloured dot" pill appeared OVER the Prayer Resources posters on a phone. Its IntersectionObserver
|
||||
// was working correctly — that is the point. The panes are `position:absolute; inset:0` in one
|
||||
// scroll-snap column, so the map pane is STACKED BEHIND the active view at full size: 100% of its
|
||||
|
|
@ -261,9 +261,9 @@ function go(id) {
|
|||
//
|
||||
// ★ SCROLLING WAS ONLY HALF THE HANDOFF, and the missing half was invisible because the
|
||||
// visible part worked: go('map') moved the reader to the map, so the action looked like it
|
||||
// had happened, while the map still showed all 27 zones. The Driver's brief for this is
|
||||
// "when I click Show on map it should be basically the same as if I searched that result in
|
||||
// the search bar" — i.e. ISOLATED, not merely scrolled to.
|
||||
// had happened, while the map still showed all 27 zones. The requirement is that
|
||||
// Show on map behaves as if that result had been searched in
|
||||
// the search bar — i.e. ISOLATED, not merely scrolled to.
|
||||
//
|
||||
// The map half was already built and waiting: upg-100-list.vue listens for 'doxa:select-zone'
|
||||
// ON ITS OWN ELEMENT (not on window, so several embeds never cross-talk) and routes it through
|
||||
|
|
@ -305,7 +305,7 @@ onMounted(() => {
|
|||
paneEls[pane.id] = el
|
||||
column.mountEl(pane.id)?.appendChild(el)
|
||||
}
|
||||
// ★ THE FRESH-LOAD CASE, which is the one the Driver actually hit. Without this the panes carry no
|
||||
// ★ THE FRESH-LOAD CASE, which is the one actually reported. Without this the panes carry no
|
||||
// inert attribute at all until the reader touches the switcher, so the pill is over the posters on
|
||||
// ARRIVAL — before any view change has happened. A signal that only fires on transitions never
|
||||
// describes the initial state.
|
||||
|
|
@ -357,7 +357,7 @@ useShadowStyles(PANE_GRID_STYLES + `
|
|||
background:var(--ui-bg); color:var(--ui-text);
|
||||
font:14px/1.4 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,system-ui,sans-serif;
|
||||
}
|
||||
/* NO AUTO-DARK. The DX design pattern is material LIGHT (Driver 2026-09-20): a system set to dark
|
||||
/* NO AUTO-DARK. The DX design pattern is material LIGHT (decided 2026-09-20): a system set to dark
|
||||
mode must NOT flip this element. The prefers-color-scheme:dark override that used to live here is
|
||||
removed deliberately — the light palette above is the only theme. */
|
||||
/* ── header: same height/border as the shell; switcher centred; share at right ── */
|
||||
|
|
|
|||
Loading…
Reference in a new issue