feat(street-level): one colour per imagery source

As more sources arrive, each one reads the same everywhere it appears: its
PROVIDERS chip, coverage lines, overview points, image cones and a single
legend swatch. The palette lives in one place, PROVIDER_COLORS in
src/layers/streetLevel/policy.js: Mapillary green (#05cb63), Panoramax
purple, Google Street View blue.

Mapillary drops its age dimming and magenta panoramas: lines and overview
points are Mapillary green, cones are green too and 360° images keep their
ring shape; the 360°/FLAT filter still separates them, and the selected
sequence stays GEV cyan.

The provider contract loses `legend`: `colors.coverage` is the source's one
colour, and the core composes the legend as one swatch per active source
(its name, its colour) followed by Selected. The chip group takes an
optional per-chip colour as a `--chip-color` CSS variable, written only
when it changes; Street Level chips show it as a swatch dot and, while on,
as the chip's border, text and a faint fill.

The feature gate checks the legend has one swatch per source plus Selected
and that the Mapillary chip carries #05cb63.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-09-29 00:03:00 +01:00
1 parent d5da45eacb
commit 0f2cac2ca2
21 files changed
+188 -99

No files matched your search

+6 -5
View File
@@ -7,10 +7,9 @@
* @property {string} label Chip text and link label, e.g. 'MAPILLARY'.
* @property {string|null} requiresKeyId Key-setup id the provider needs, or null when keyless.
* @property {string} pickPrefix Every primitive id the provider creates starts with it.
* @property {{coverage: string, coverageOld?: string, pano?: string}} colors
* @property {{coverage: string}} colors The source's one colour (policy.js PROVIDER_COLORS): chip, lines, points, cones and legend.
* @property {{key: string, html: string}} credit On-globe attribution while the provider is active.
* @property {{coverage: 'tiles'|'bbox'|'none', sequences: boolean, pano: boolean, capturedAt: boolean, creator: boolean, follow: boolean}} capabilities
* @property {Array<{key: string, label: string, color: string}>} legend Coverage colour key.
* @property {(imageId: string) => string} externalUrl Deep link to the image on the provider's site.
* @property {(context: ProviderContext) => ProviderInstance} create
*
@@ -80,7 +79,6 @@ const REQUIRED = Object.freeze([
'colors',
'credit',
'capabilities',
'legend',
'externalUrl',
'create',
]);
@@ -127,9 +125,12 @@ export function validateProviders(providers) {
);
if (!provider.credit?.key || !provider.credit?.html)
throw new TypeError(`Street Level provider ${label} needs a credit`);
if (!Array.isArray(provider.legend))
if (
typeof provider.colors?.coverage !== 'string' ||
!provider.colors.coverage
)
throw new TypeError(
`Street Level provider ${label}: legend is not a list`,
`Street Level provider ${label} needs a coverage colour`,
);
ids.add(provider.id);
prefixes.push(provider.pickPrefix);