Renamed from F to avoid colliding with the pilot's show-header arm F. A phone +
a non-withheld desktop-only large sheet via countryPosterType rejects
poster.phoneLimitAll (records) and reads it (unsupported); poison-proven by
neutralising phoneBlocked.
Closed, the folder sat mid-drawer under the one short Posters section with
empty space below, so it read as a caption with nothing under it and was easy
to miss on a phone. Open, its plugins show like any section's rows; a reader
can still collapse it. check:posterverse-shelf asserts the open default, and
its self-test plants a closed folder.
The samples arm is now a function the self-test can poison: with withholding
switched off, India (no sheet tier, no outlines) must fail as a sample that
cannot be drawn. Closes the branch the arm had no poison for.
India, the one large-sheet country, now prints at 5 x 8 ft only, so the
5.5 x 5.5 ft and 3 x 5 ft side-lane cards would have drawn India's 60x96 sheet
under their own label. They now sample the largest drawable countries (China
at 66x66, Sudan at 36x60) through the same shared sheet path, and their
details say that no country prints at this size today
(posterDesign.missing.noCountryAtSize, 11 locales). The 60x96 card keeps
India and stays withheld. check:poster-design-catalogue now checks every
sample: drawable, or withheld by countrySheet.js.
A host that serves the tree with Last-Modified and no Cache-Control lets a browser run its cached files for
hours. Measured: a typed-URL revisit after a deploy requested none of the gallery, the manifest or the bundles,
so a returning reader ran the old files, or an old manifest against a new bundle.
- The manifest is fetched with cache 'no-cache', so it is always revalidated (a 304 when unchanged).
- A fingerprint of the manifest (every bundle's integrity) is kept per tree. When it differs from the reader's
last visit, every bundle the browser already holds gets one conditional request before any tab mounts:
a 304 refreshes it, and a 200 is cancelled at its headers, which drops the stale copy. The plain <script>
that follows, the gallery's own or a composite's for its children, then runs the new file at the same URL.
- A version query is not used: loadScriptOnce keys its registry by the full href, so X.js?v= next to a
composite's plain X.js would run the bundle twice.
- A first visit with an empty cache costs nothing extra (only-if-cached finds nothing held), and so does an
ordinary visit with an unchanged manifest. The mount waits at most 8 s for the check.
A preset page loads only poster-sections.js, so a preset of a poster type
registered by another plugin's script (upg100-list) could only say No
posters. The generator now gives no presets to a type whose file is not on
the tree, and check:poster-sections fails on any such preset: 9 presets
remain (7 regions, all regions, configure).
The tree generator writes a page per preset but never deletes one, so the
dropped presets' pages (the 10 upg100 ones and countries-other) are removed,
and a new arm holds the built preset pages to exactly the presets.
The pilot's country data now places every country in a real region, so the
country type has 7 regions. gen:poster-sections-presets regenerated the list
(19 presets); check:poster-sections had failed on the stale countries-other
preset and passes now. Bundle, tree and SRI rebuilt.
gen:poster-sections-presets writes poster-sections.instances.json from each
type's scopes() (all regions, then one preset per region titled by the type's
own section title in the site's English, then configure), the same list
check:poster-sections holds the file to. Today it reproduces the 20 presets
exactly, in the data's region order; when the data's regions change, running
it is the whole fix.
A poster plugin now lists its designs in its own <profile>.meta.json under
posterDesign ({ family, name, size, mobileFriendly, labelKey?, instance? },
one or an array). internal/profile-meta.mjs carries the block into
manifest.json only when present, so every existing manifest keeps its bytes
(the built and the dev manifest share that function).
Poster Design's Styles reads the tree's manifest at runtime and gives each
declared design a card under From other plugins: the card says it is made by
its own plugin, and Show full details shows that plugin's page (or preset
page) live, with a link to open it. No plugin code is imported, and a plugin
adding its block does not rebuild Poster Design.
check:poster-design-catalogue now also validates every block, keeps names
unique across templates.js and all blocks, and keeps a names ledger
(src/catalogue-names.json): a shipped name that is renamed or removed fails.
Its new self-test (in gates:pre) proves an invalid block, a name clash, a
renamed ledger name and a missing preset each fail, and the documented
example passes.
Each country's region is set to the live wagf_region its groups carry (file
label spelling); only the region field changes (stats, bbox, count, India row
untouched). The stale 'Other' bucket (18 countries) empties out and each moves
to its real region: USA and the non-Spanish Caribbean + the Guianas to North
America & Non-Spanish Caribbean; Andorra/Monaco/San Marino to Europe; Cocos,
Christmas, Niue, Norfolk, Tokelau, Wallis & Futuna to Oceania. Europe goes
27 -> 30, matching the live Regions dashboard.
check:poster-section drops 'other' from its covering scopes (the bucket is now
empty, so the /regions/other scope resolves to zero countries); the seven real
regions still cover all 175 countries exactly once.
Region-only diff (18 region lines); B25 India withheld unchanged
(check:country-sheet-one-path, poster-section, poster-browser-equiv,
poster-design-catalogue all PASS).
The presets file now has to hold, per poster type, exactly an all-regions
preset plus one per region the type's adapter returns from scopes(). A region
the data drops (a stale "other") or gains then fails the check, instead of
lingering as a preset. 'country' is measured always; 'upg100-list' once its
type file is on the tree. Self-test: a preset for a region the type does not
have must fail.
Poster Sections becomes a parameterized profile like doxa-simple-map: its
props (type, scope with 'all' = every region, locale, columns, collapsed)
choose the <dxm-poster-section> sections it renders, and
poster-sections.instances.json lists 20 presets, each built into its own page
and embed snippet in manifest.json:
- country posters: all regions + each of DX Life's /regions regions (8);
- UPG 100 posters: all regions + each of its 9 regions (they show once the
upg100-list type is registered on the page);
- configure: the panel of choices and Copy embed, for making new presets.
The element's columns attribute now comes from the library (landing
separately), so this commit drops its library edits; check:poster-sections
reads columns back through that attribute and passes once it is on the pilot.
A new bundle, plugin-designs/poster-sections, with one profile, Poster Sections.
A panel of choices (poster type from the registry at runtime, region or All
regions, language, posters per row, start collapsed) drives the library's
<dxm-poster-section> elements shown live below it, and Copy embed gives the
bundle script plus the exact tags for those choices, with absolute URLs so they
work on another site. The bundle also registers <dxm-poster-section>, so a site
that loads only poster-sections.js can place the tags.
Library: the element takes a columns attribute (posters per row, 1-5), passed
to the wall's existing columns prop; absent keeps today's default, so existing
importers are unchanged. New catalogue area posterSections in all 11 locales.
check:poster-sections (in gates:pre) reads every panel choice back through the
element's own attribute reader; its self-test proves a builder that drops
columns, drops collapsed, or repeats a region under All regions fails.
previewUnreleased() is import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true, and the
India tab uses the same expression inline, so every BUILT bundle compiles both away. A page, an embed or a reader's
console setting the global can no longer print the withheld poster from production; only the Vite dev server (the
pilot's Plugin Studio, whose element bootstrap sets it) previews it. posterWithheld()/largeSheet() take the preview
state as an optional argument, so a gate can test an open preview directly.
check:country-sheet-one-path arm G: a global alone does NOT open the preview outside DEV; withheld by default; an
open preview prints 66x66; the India tab is gated on the DEV read; and the built tree mentions the flag 0 times
(the stale ungated build failed this check, as it should). A poison for each.
The view places the library's <dxm-poster-section type="country"> once per region the poster data files its
countries under, in the dashboard's region order, starting shut, in the page's locale. Rebuilt regions-dashboard,
then the tree (the new leaf page) and SRI.
It is not released yet, and the pilot syncs to DX Life (and the partner handoff) within minutes. So WITHHELD.IND is
{ poster: true, rows: true, preview: true }: posterWithheld() is false ONLY when the host page sets
globalThis.DXM_PREVIEW_UNRELEASED === true (the Plugin Studio on the pilot, where it is reviewed); never by a URL
parameter. Every other host keeps the withheld sentence on India's card, and Country Posters' India tab exists only
under that flag. The rows (list, packet, cards) stay withheld everywhere.
check:country-sheet-one-path arm G proves (a) withheld by default and (b) printing at 66x66 with the flag (only
=== true opens it), rows withheld in both, the India tab gated, with a poison for each; check:poster-section arm C
expects the sentence by default.
countrySheet.js WITHHELD now says WHAT of a whole country is withheld: { IND: { poster: false, rows: true } }, with
posterWithheld() and rowsWithheld() (sheetWithheld() keeps meaning the poster, the name Poster Design reads).
- India's whole-country POSTER prints again, 66x66 by default (60x96 and 36x60 on request); largeSheet() names it.
- Its ROWS (the prayer list, packet and cards: about a thousand pages) stay withheld with the same sentence, in the
wall, the Regions list and the 'country' poster type. India's state posters are unchanged.
- Country Posters gains an INDIA tab: the same wall filtered to India, labelled with India's own name in the
reader's language (Intl), between the posters and the map.
check:country-sheet-one-path arm G checks the split (poster live at 66x66, rows withheld, state cards never
withheld, both callers read the right half), with poisons for un-withheld rows and a wall that prints them;
check:poster-section arm C follows. The by-state, leaderless India layout follows as its own PR.
The section the Country Posters tab draws (a collapsible region head + its poster cards) is now usable on its
own, for any poster set, by parameters:
- library/components/poster/posterTypes.js: a TYPE REGISTRY. A type is an adapter (items for a scope, the
section's title, the print actions). The library registers 'country' (library data + the shared sheet path,
so a withheld whole-country poster stays withheld); plugins register their own types from their own code
(UPG 100 lists, regions, continents later). No plugin code in the library.
- 'country' scopes: a region key in any spelling. DX Life's /regions slug ('latin-america-and-caribbean'),
the API's value, the wall's slug and the English name all reduce to one canonical key.
- PosterSectionView.vue: one section, drawn by a PosterWall in a new SECTION-ONLY mode (no wall head; off by
default, so every existing wall renders as before), embedded so the page scrolls it.
- <dxm-poster-section type scope collapsible collapsed locale assets-base api-base-url is-dark>: the
attributes map onto the standard element's profile-config (posterSectionConfig.js, pure), so it keeps the
per-instance i18n and store. registerPosterSectionElement() from any bundle; Country Posters ships it first.
- country-posters/poster-section: a page showing two sections (Africa open, Oceania collapsed).
- poster.sectionNone (11 locales): an unknown type or an empty scope says so, never a blank.
check:poster-section (gates:pre) + self-test: the registry, DX Life's 8 scopes covering every country once,
withheld India, the attribute mapping, and PosterSection.vue injecting every name its template uses; 3 poisons.
The known collapse-while-scrolled landing is left exactly as it is.
A host page can define a tag name before our bundle loads, and the first definition wins. DX Life's own
/js/doxa-simple-map.js defines <doxa-map> and <doxa-simple-map>, so on those pages our guarded define was
skipped and the page drew the host's element under our name.
- The doxa-simple-map bundle now defines <dxm-simple-map>, DX Maps' own name, first. The manifest, the leaf
pages and the gallery mount it (generate-tree reads the first define).
- <doxa-map> and <doxa-simple-map> are defined only when the host asks: data-legacy-tags on the bundle's
<script>, or window.DXM_LEGACY_TAGS = true before it loads.
countrySheet.js now withholds the whole-India poster (WITHHELD,
sheetWithheld), and makeCountrySheet returns nothing for India. The three
large-format Styles cards use India as their sample, so they showed
"preview unavailable" and their print fell through to a stored poster
that does not exist. They now answer PosterWall's unsupported with
messages.countryPosterTooMany, in the reader's language: the card shows
that sentence instead of a preview, and a print opens only that notice.
The designs stay in the catalogue, so they return when India is no longer
withheld.
India's whole-country poster does not ship yet; a solution is in progress. countrySheet.js gains WITHHELD (India)
and sheetWithheld(item):
- makeCountrySheet draws nothing for a withheld country, in any format, before fetching a single record.
- largeSheet() is null for it, so the Country Posters wall and the Regions list show their existing
messages.countryPosterTooMany ("Too many people groups to fit on one poster legibly — a solution is in
progress.", all eleven locales) on the card instead of a poster.
- Both callers also withhold its list, packet and cards with the same sentence (about a thousand pages, offered
when the poster is).
- India's STATE and union-territory posters are separate cards (indiaState) and are untouched; so are the India
section and its first place.
- sheetFormatFor keeps the large-sheet size rule itself (it returns with the poster).
check:country-sheet-one-path arm G: India stays withheld (no sheet in any of 7 formats, 0 records fetched, its
state cards never withheld, both callers show the sentence and reject its rows), with a poison for un-withholding
it and one for a wall that prints its list.
Measured on the shipped India sheets in the browser (getBBox, Lato loaded): 74 of 151 photos on the 60x96 and
32 on the 66x66 overlapped their own name, by up to 7 px (half a letter at 20.7 pt). The photo was placed from
an ESTIMATED text width, and the real width runs wider.
Now the photo sits in a fixed slot on the LEADER side of its name, and the name moves out by the slot. A
side-lane name is anchored at its leader-side end (left column: text-anchor end; right: start), so its glyphs
lie wholly on the far side of that anchor and the photo wholly on the near side: they cannot overlap whatever
width the font draws. Only a name with a photo takes the slot, so the columns and the type keep their size
(India 60x96 stays 20.7 pt).
Measured after: India 60x96 and 66x66 have 0 photos on, and 0 touching, their name. 0 proper crossings on all
three India formats. 36x60 (no photos) is byte-identical. Re-pinned.
- check:side-lanes: every photo on its name's anchor side, at the 4 px gap (a source twin, always measured).
A self-test poison restores the estimated-width placement: 167 photos off their side, caught.
- check:side-lane-photo-pixels (browser): every photo box clear of its name's real drawn box (getBBox), with
the same poison in its self-test.