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 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.
The measure-once design missed a host restyled from sized to height:auto after mount WITH in-flow content: it
stayed sized at its content height instead of getting the floor back, as the old probe did.
- After mount, follow() re-probes only when the reading says the host may have changed kind: floor on and the
host shorter than the floor (styled after mount, or capped), or floor off and the host changed size (it may now
follow its content). The re-probe saves every scrolled descendant (through shadow roots) and puts it back
before the frame paints, so no reader state is lost. A window-height change on a content-sized host still
writes nothing.
- A host capped below the floor (max-height) is latched by the probe that finds it, until it reaches the floor
again, so it is never re-probed in a loop. The 1 s post-flip window is gone.
- check:host-floor gains arm H (sized → auto with 420 px of content: the floor returns, at most one flip, the
scrolled child keeps its scrollTop) and counts re-probes in D/E. Poisons: the old per-resize probe, no capped
latch, no re-check of a sized host, no scroll restore.
- library/elements/hostFloor.js holds the decision ProfileLoader uses: probeOnce() at mount, follow() on every
host resize (reads only; at most one flip of the floor).
- A host CAPPED below the floor (max-height) follows its content up to the cap. It is found at mount, or, if
the cap arrives later, by the host shrinking right after a flip; either way it is latched as content-sized
and never flipped again, so it cannot loop. The post-flip check runs before the collapse rule, so a capped
host with empty content is latched rather than flipped back and forth.
- check:host-floor (gates:pre; self-test in gates:post): a stub DOM with a scrolled child that clamps when the
loader collapses. After mount, host resizes never write the loader nor move the child; a sized host fills;
late sizing flips once; a max-height cap (at mount: 0 flips; after mount: at most one flip and one revert)
never loops; a collapsed host gets the floor back; an inline host is left alone. Poisons: the old per-resize
probe, and a missing latch.
Merge studio's poster type registry + pins-in-state + preview-flag pilot into
the region data fix. Built-output conflicts resolved by taking the pilot's
public/ then rebuilding the three bundles that embed country-posters.json
(country-posters, poster-design, regions-dashboard) on the merged source, so
they carry both the registry changes and the corrected regions. Tree + SRI
regenerated. check-poster-section's dropped 'other' scope and the 18 region-only
records are preserved through the merge.
A regression from the host-sized floor: ProfileLoader re-measured its host on EVERY host resize by briefly setting
the loader's min-height to 0 and then to a huge value. On a content-sized host (100svh tall) a window-height
change, as a phone's URL bar makes, resized the host and ran that probe; collapsing and expanding the loader
clamped a scrolling child's scrollTop, and the embedded UPG-100 app, whose view follows its column's scroll,
flipped from Map back to its first view.
- The two-value probe now runs ONCE, at mount, before the profile renders.
- After that the loader only READS sizes and flips one CSS value: a block host shorter than the floor (only a
host sized from outside can be) turns the floor off; a block host that collapsed to nothing turns it back on;
a window-height change on a content-sized host changes nothing.
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.
Measured headless (390, real touch, a host on a page that scrolls): in the detail view, a drag up from the people
group's floating picture above the sheet scrolled the PAGE (scrollY 0 to 135, en and ar), while the same drag from
the sheet's strip did not. The picture sits outside the sheet and its grab lip, so it had none of their handlers.
It now takes the sheet's capture-phase touch handlers and touch-action:none: a drag from it resizes the sheet (the
grab-zone test holds above the sheet, as for the lip), and a tap is handed back as a click that reaches its
existing @click (open the sheet fully), as before.
scrollTo(paneId) called target.scrollIntoView(), which scrolls every scrollable
ancestor, the embedding page included. On another site, "Map" / "Show on map"
moved the host's own page so that the map's top met the window's top, and a host
shorter than the window then showed its white space under the map. The column
is the scroller (position:absolute; overflow-y:auto) and each pane's
offsetParent, so col.scrollTo({ top: target.offsetTop }) lands the pane exactly
and leaves the page alone.
Tightened before the India tab lands. A built bundle that READS globalThis.DXM_PREVIEW_UNRELEASED would ship the
withheld path itself, behind a global that any embedding page or console can set before the element loads, so
withheld content would print from production.
- A: no file under public/ names the flag at all (was: no assignment).
- B: every source line that reads OR writes it carries import.meta.env.DEV (was: writes only), so each read
compiles away in a build.
- Self-test: a built setter, a built read, an unguarded source setter and an unguarded source read must each
fail; the DEV-guarded forms and comments pass.
Reported from a phone (Home and Pray, the people-group detail sheet): dragging the handle down worked until the
sheet became shorter than its content; from then on the page scrolled while the finger kept dragging.
- The sheet and the detail panel's scrollers (.people-group-detail, .detail-content) contain their overscroll,
so a list that reaches its end can never chain a scroll to the page.
- The drag handlers on the sheet and its grab lip listen in the CAPTURE phase, so every touch reaches the drag
first and no child's handler can stop one from being claimed.
country-posters.json is statically imported by these three bundles (via
countryPosterType.js, poster-styles.vue and regions-list.vue), so the region
re-derive stales their built output. Rebuild the three, regenerate the tree
(manifest + leaf pages) and refresh SRI. The composite-freshness gate named
exactly these three; the other 26 bundles are unaffected. Only these three
bundle .js change; the doxa-maps-spa leaf updates its embedded integrity hash
for country-posters.
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.
- ONE REGISTRY PER PAGE. Every bundle inlines its own copy of posterTypes.js, and <dxm-poster-section> is
defined by whichever bundle loads first, so with a module-level Map that element saw only its own bundle's
types: type="upg100-list" rendered "none" when the country-posters bundle had defined it. The registry now
lives on the page (globalThis.__DXM_POSTER_TYPES__, created once): every copy registers into it and reads
from it; a name registered twice keeps the first (a dev warning; the built-in country is silent); every
registration is announced on window (dxm-poster-type-registered), and PosterSectionView looks again when
its type arrives after it asked.
- COLUMNS (carried from the Poster Sections plugin PR so the element's library lands once): columns="1".."5"
maps to profile-config.columns and to PosterWall's existing columns prop; absent or out of range is
today's default, and a reader's saved per-row choice still wins.
- check:poster-type-registry (gates:pre; self-test in gates:post): two independent copies of the module side
by side, as two bundles on one page; a type registered through one is found through the other; first wins;
announced. The self-test's per-bundle registry fails.