upg-100: no mapbox stylesheet under maplibre, and assert the preview exists
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
Two things, both found by other agents' reports rather than by my own gates. rega found upg-100-list.vue:1464 injecting mapbox-gl.css v3.3.0 into the shadow root unconditionally, so a keyless maplibre embed still pulled a stylesheet from api.mapbox.com. Token-free and harmless to render, but wrong-engine leakage on a page whose point is having no Mapbox dependency. Gated on isMaplibre, which already existed at line 119. This commit is ATOMIC by design: the fix, the rebuilt bundle, and the deletion of rega's MAPBOX_KNOWN exemption land together. Either half alone leaves a window — delete first and main red-gates on its own known bug; fix first and a stale exemption sits there hiding the next one. Proven before deleting, because the exemption is keyed on a URL STRING while the defect is a network REQUEST: the built bundle still contains the v3.3.0 literal (the constant remains; the guard stops the fetch), so a source-grep gate would have been misled. rega's gate measures requests, and it reported "provider=maplibre made 0 request(s) to api.mapbox.com" in BOTH js and iframe modes with no KNOWN line printed at all — i.e. the exemption was already dead code. Re-proven after deletion: still 0 requests, still PASS. The leaf-head preloads are explicitly NOT covered and the reason is recorded in the gate: mapbox-gl.css v3.24.0 and mapbox-gl-geocoder v5.0.0 are a different version and a different owner (generate-tree, bundler-1's lane), already tracked separately. Second: the Driver's report had two halves — "it is supposed to show the previews" and the print options overlapping. I fixed and gated the overlap and left the first unasserted. The over-preview check was written as `if (img)`, which means a card with NO preview satisfies it perfectly: nothing can overlap an image that is not there. The gate now counts previews. Mutation-tested by aborting every previews/** request: the product degrades well — all 27 cards swap to a "poster not generated yet" placeholder with no broken-image icons and no <img> at all, which is precisely why an image-health check cannot see it. So the fallback itself is asserted, and all-cards-placeholder now fails. A first version of that assertion was wrong and the reason is recorded in the code: it called 18 lazy-loaded previews broken. Every img carries loading=lazy, the first unpainted one sat 3926px down an 844px viewport, and no HTTP response failed. Deferred is not broken; it now judges only in-viewport images.
This commit is contained in:
parent
460ccb564b
commit
25e3edec8f
9 changed files with 138 additions and 81 deletions
|
|
@ -55,6 +55,7 @@ async function measure(width) {
|
|||
const cards = all.filter((e) => (e.className||'').toString().split(' ').includes('card')).filter(vis)
|
||||
const boxes = cards.map((c) => ({ c, b: c.getBoundingClientRect() }))
|
||||
let intrusions = 0, overflowing = 0, worst = 0, coveringPreview = 0, worstY = 0
|
||||
let previewsPainted = 0, previewsBroken = 0, noPreview = 0
|
||||
for (const { c, b } of boxes) {
|
||||
const row = c.querySelector('.pfs')
|
||||
if (!row) continue
|
||||
|
|
@ -68,7 +69,24 @@ async function measure(width) {
|
|||
// 35px -> 78px, and because it sits at bottom:-14px the extra height pushed its TOP from
|
||||
// 216px up to 173px in a 238px card: over the preview image and the card's outline.
|
||||
// Horizontal clean, card visibly broken, gate green.
|
||||
// COUNT THE PREVIEW, do not merely guard on it. An if-guard protects this loop from a
|
||||
// placeholder card — and also means a card with NO preview passes the over-preview assertion
|
||||
// perfectly. The driver reported BOTH a missing-previews symptom AND the overlap;
|
||||
// measuring only the overlap leaves the first unasserted.
|
||||
// NOTE: no backticks in this block — it lives inside a JS template literal.
|
||||
const img = c.querySelector('img')
|
||||
// ONLY JUDGE WHAT IS IN VIEW. Every preview carries loading=lazy, so a card below the fold
|
||||
// legitimately has naturalWidth 0 — measured at 390px: 8 of 27 painted, the first unpainted
|
||||
// one 3926px down an 844px viewport, and ZERO failing HTTP responses. A first version of this
|
||||
// assertion called that 18 broken images. Deferred is not broken; only an in-viewport image
|
||||
// that never painted is.
|
||||
if (!img) noPreview++
|
||||
else {
|
||||
const ib2 = img.getBoundingClientRect()
|
||||
const inView = ib2.top < window.innerHeight && ib2.bottom > 0
|
||||
if (img.complete && img.naturalWidth > 0) previewsPainted++
|
||||
else if (inView) previewsBroken++
|
||||
}
|
||||
if (img) {
|
||||
const ib = img.getBoundingClientRect()
|
||||
if (ib.height > 2 && rb.top < ib.bottom - 1) {
|
||||
|
|
@ -87,6 +105,7 @@ async function measure(width) {
|
|||
const tiny = ctrls.filter((e) => { const b = e.getBoundingClientRect(); return b.width < 18 || b.height < 10 })
|
||||
return { cards: cards.length, rows: cards.filter((c) => c.querySelector('.pfs')).length,
|
||||
overflowing, worst, intrusions, coveringPreview, worstY,
|
||||
previewsPainted, previewsBroken, noPreview,
|
||||
controls: ctrls.length, tiny: tiny.length }
|
||||
})()`)
|
||||
await page.close()
|
||||
|
|
@ -107,6 +126,8 @@ for (const r of results) {
|
|||
+ ` · overflow ${r.overflowing}${r.worst ? ` (${r.worst}px)` : ''}`
|
||||
+ ` · neighbour ${r.intrusions}`
|
||||
+ ` · over-preview ${r.coveringPreview}${r.worstY ? ` (${r.worstY}px)` : ''}`
|
||||
+ ` · previews ${r.previewsPainted} painted`
|
||||
+ `${r.previewsBroken ? `/${r.previewsBroken} BROKEN` : ''}${r.noPreview ? `/${r.noPreview} none` : ''}`
|
||||
+ ` · ${r.controls} control(s), ${r.tiny} too small`)
|
||||
}
|
||||
|
||||
|
|
@ -125,6 +146,27 @@ for (const r of results) {
|
|||
}
|
||||
// The row belongs BELOW the preview. Driver-reported 2026-09-24: "the download print options are
|
||||
// not showing up under the blue outline, they are overlapping where the blue outline would be".
|
||||
// THE PREVIEW MUST ACTUALLY BE THERE. A card with no image passes every geometry assertion
|
||||
// above trivially — nothing can overlap what does not render. Driver 2026-09-24: "it is supposed
|
||||
// to show the previews".
|
||||
if (r.previewsBroken) {
|
||||
fails.push(`${at}: ${r.previewsBroken} preview image(s) failed to load (naturalWidth 0) — `
|
||||
+ 'a broken preview is not something the geometry checks can see')
|
||||
}
|
||||
// EVERY card falling back to the placeholder means the previews are gone. MEASURED by aborting
|
||||
// every previews/** request: the product degrades WELL — all 27 cards swap to a .ph placeholder
|
||||
// reading "poster not generated yet", with no broken-image icons and no <img> left at all. That
|
||||
// is correct behaviour and exactly why it is invisible to an image-health check: previewsBroken
|
||||
// stays 0 because there is nothing left to be broken. Assert the FALLBACK instead.
|
||||
if (r.cards && r.noPreview === r.cards) {
|
||||
fails.push(`${at}: all ${r.cards} card(s) fell back to the "poster not generated yet" `
|
||||
+ 'placeholder — no preview rendered anywhere, and the geometry assertions above are vacuous '
|
||||
+ 'without one (they cannot overlap an image that is not there)')
|
||||
}
|
||||
if (r.previewsPainted === 0 && r.cards && r.noPreview !== r.cards) {
|
||||
fails.push(`${at}: NOT ONE preview image painted across ${r.cards} card(s) — the geometry `
|
||||
+ 'assertions above are vacuous without them')
|
||||
}
|
||||
if (r.coveringPreview) {
|
||||
fails.push(`${at}: ${r.coveringPreview} print row(s) COVER the preview image `
|
||||
+ `(top sits ${r.worstY}px above the image's bottom edge) — growing the row taller to fit it `
|
||||
|
|
|
|||
|
|
@ -49,9 +49,18 @@ const PROVIDER = argOf('--provider', '')
|
|||
// KNOWN, ROUTED exemptions from the zero-Mapbox rule — each names its owner. Printed on every run so
|
||||
// it cannot become invisible; DELETE the entry when the owner lands the fix (the gate then enforces it).
|
||||
const MAPBOX_KNOWN = {
|
||||
// my-upg-100-list/upg-100-list.vue:1464 injects a pinned Mapbox stylesheet regardless of engine.
|
||||
// Token-free static CSS (no tiles/styles/auth), routed to upg100-plugin via dx-shepherd 2026-09-24.
|
||||
'https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css': 'upg100-plugin (upg-100-list.vue pins mapbox-gl.css)',
|
||||
// EMPTY ON PURPOSE. The one entry here — my-upg-100-list/upg-100-list.vue:1464 pinning
|
||||
// mapbox-gl.css v3.3.0 regardless of engine — was fixed and the exemption deleted in the SAME
|
||||
// COMMIT as the fix, so main never had a window where the gate red-flagged its own known bug and
|
||||
// never had a window where a stale exemption hid a live one. Proven before deletion: this gate
|
||||
// reported "provider=maplibre made 0 request(s) to api.mapbox.com" in BOTH js and iframe modes,
|
||||
// with no KNOWN line printed at all.
|
||||
//
|
||||
// ⚠ NOT COVERED BY THIS MAP, and deliberately so: the generated leaf HEAD still preloads
|
||||
// mapbox-gl.css v3.24.0 and mapbox-gl-geocoder v5.0.0. Different versions, different owner —
|
||||
// that is generate-tree (bundler-1's lane), tracked as
|
||||
// BUG-LEAF-PAGES-PRELOAD-MAPBOX-EVEN-UNDER-MAPLIBRE. This gate embeds the GALLERY, which does not
|
||||
// load those leaves, which is why it reads zero today.
|
||||
}
|
||||
// ★ FREE PORTS, ASKED OF THE OS — never fixed ones. A stray server left on a fixed port by an
|
||||
// earlier crashed run answered in place of the tree under test, and this gate PASSED a known-broken
|
||||
|
|
|
|||
|
|
@ -1458,8 +1458,14 @@ onMounted(async () => {
|
|||
// custom MapToolbar buttons never needed mapbox ctrl CSS, so this profile
|
||||
// skipped it until the shared GeocoderComponent (a real mapbox ctrl) arrived.
|
||||
// Mirrors src/composables/useMapInstance.js ensureMapboxCSS (idempotent).
|
||||
// ⚠ MAPBOX ONLY. This ran unconditionally, so a keyless maplibre embed still pulled a
|
||||
// stylesheet from api.mapbox.com — token-free and harmless to render, but wrong-engine
|
||||
// leakage on a page whose whole point is having no Mapbox dependency. It was the last
|
||||
// mapbox-under-maplibre reference in this profile, carried as a named exemption in the
|
||||
// maplibre-embed gate; guarding it lets that exemption be deleted and enforced instead.
|
||||
// MapLibre ships its own ctrl CSS via ensureEngineScript, so nothing is lost.
|
||||
try {
|
||||
const shadow = mapEl.value && mapEl.value.getRootNode()
|
||||
const shadow = !isMaplibre && mapEl.value && mapEl.value.getRootNode()
|
||||
if (shadow instanceof ShadowRoot) {
|
||||
const MAPBOX_CSS = 'https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css'
|
||||
if (!shadow.querySelector(`link[href="${MAPBOX_CSS}"]`)) {
|
||||
|
|
|
|||
2
public/doxa-maps/index.html
generated
2
public/doxa-maps/index.html
generated
File diff suppressed because one or more lines are too long
8
public/doxa-maps/manifest.json
generated
8
public/doxa-maps/manifest.json
generated
|
|
@ -681,7 +681,7 @@
|
|||
"bundle": "my-upg-100-list",
|
||||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"output": "doxa-maps/my-upg-100-list/my-upg-100-list.js",
|
||||
"integrity": "sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT",
|
||||
"integrity": "sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf",
|
||||
"elements": [
|
||||
"my-upg-100-list-map"
|
||||
],
|
||||
|
|
@ -701,7 +701,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -715,7 +715,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -729,7 +729,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
138
public/doxa-maps/my-upg-100-list/my-upg-100-list.js
generated
138
public/doxa-maps/my-upg-100-list/my-upg-100-list.js
generated
File diff suppressed because one or more lines are too long
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-/KD0HoAzJJzY6I+NIzT7od3PI0JIlzs7v8DLjTaeRQFYjbePUfnO3xDeNy3Q9yAT" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue