From 1b2c5960114b90fc01780a31b5241f54ecc80362 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 03:25:22 +0000 Subject: [PATCH] The share menu's tabs mirrored the wrong way, and the check could not see them Five physical CSS declarations in the chrome every production map renders, so under Arabic each of them lands on the wrong side for every map at once: SemanticTreeLegend's active tab chip drew its border on the left, the share menu's step list padded from the left, its tab spacing and its auto-spacer both pushed from the right, and the geocoder's search icon sat at left:7px inside an input that mirrors. All five become logical properties, which in a left-to-right reading resolve to exactly what they resolved to before: four insertions against four deletions in the components, one property each, and the English view cannot move. The reason they survived is the more useful half. check-legend-logical-insets existed and passed, but it read only library/components/legends -- the share menu is one directory away, and a reader in Arabic does not know which folder a control came from. So the scan now covers map-controls as well. Widening it was not enough. It still passed, because its CSS extractor matched a template literal with the pattern `([^`]*\{[^`]*\}[^`]*)`, which describes a literal containing ONE brace pair. ShareButton keeps its styles in a 600-line useShadowStyles literal with hundreds, so the scan captured seven fragments totalling 7,003 characters of a 629-line file and never saw .share-tab at all. It was reporting PASS on a directory it was barely reading, which is worse than having no check: a green gate is read as evidence. Template literals are now taken whole and kept only when they look like CSS -- 2 blocks, 8,462 characters, and the rule is finally in scope. The self-test poisons BOTH directories. Poisoning only a legend would have proved the check works on the half it always covered, and passed unchanged the day the wider scan silently narrowed again. Its map-controls arm puts back the exact declaration this commit removes, and its failure message says what a failure means. --- internal/check-legend-logical-insets.mjs | 68 ++++++++++++++++--- .../components/legends/SemanticTreeLegend.vue | 2 +- .../map-controls/GeocoderComponent.vue | 2 +- .../components/map-controls/ShareButton.vue | 6 +- 4 files changed, 62 insertions(+), 16 deletions(-) diff --git a/internal/check-legend-logical-insets.mjs b/internal/check-legend-logical-insets.mjs index 8f7bc5168..b365b4484 100644 --- a/internal/check-legend-logical-insets.mjs +++ b/internal/check-legend-logical-insets.mjs @@ -22,14 +22,26 @@ import { join, dirname } from 'node:path' import { fileURLToPath } from 'node:url' const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..') -const DIR = join(ROOT, 'library/components/legends') +// ★ THE SHARED CHROME, NOT ONLY THE LEGENDS. This check began as a legends check, and that scope is +// exactly why three physical declarations survived in ShareButton — the share menu is chrome every +// production map renders, but it lives one directory away and was never read. A reader in Arabic +// does not know which folder a control came from. Audited 2026-09-29: ShareButton had padding-left +// on the step list and margin-right on both the tab spacing and the auto-spacer, so under `ar` the +// gap fell on the wrong side of every share tab. +const DIRS = [ + join(ROOT, 'library/components/legends'), + join(ROOT, 'library/components/map-controls'), +] +// Files are addressed as "/" so two directories cannot collide on a base name and so +// the ALLOWED keys below stay unambiguous. +const shortOf = (dir, f) => `${dir.split('/').pop()}/${f}` // file -> substrings of declarations that may stay physical, each with its reason. const ALLOWED = { - 'LegendMobile.vue': [ + 'legends/LegendMobile.vue': [ ['.mapboxgl-ctrl-attrib.mapboxgl-compact{margin-right', "the engine's own attribution control, which the engine places"], ], - 'LegendProgressBar.vue': [ + 'legends/LegendProgressBar.vue': [ ['.lpb-pop{', 'the popover is placed by JS (popLeft) over the bar segment it describes'], ], } @@ -38,7 +50,19 @@ const PHYSICAL = /(^|[;{\s])((?:padding|margin)-(?:left|right)|left|right)\s*:\s function findings(file, src) { const out = [] - const styles = [...src.matchAll(/]*>([\s\S]*?)<\/style>|`([^`]*\{[^`]*\}[^`]*)`/g)].map((m) => m[1] ?? m[2]) + // ★ THE BACKTICK ARM USED TO MISS MOST OF A BIG SHADOW-CSS LITERAL, AND THAT IS HOW THREE PHYSICAL + // DECLARATIONS HID IN ShareButton. `([^`]*\{[^`]*\}[^`]*)` matches a literal containing ONE brace + // pair; a 600-line useShadowStyles(`…`) block has hundreds, so the scan captured 7 fragments + // totalling 7,003 chars of a 629-line file and never saw .share-tab's margin-right at all. The + // gate then reported PASS on a directory it was barely reading — the worst kind of green. + // A