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

+10 -16
View File
@@ -13,7 +13,7 @@ import { COLORS } from './policy.js';
* @property {number} count
* @property {string} hint
* @property {string|null} error
* @property {Array<{key: string, label: string, color: string}>} legend
* @property {string} color The source's one colour.
*/
/**
@@ -37,9 +37,9 @@ export function summarizeCoverage(providers) {
* Compose the snapshot the panel renders from the core state and one
* snapshot per registered provider. Pure, so the merge rules are testable:
* counts add up across active providers, the layer is key-gated only when
* every switched-on provider lacks its key, and the legend lists each active
* provider's colours (prefixed by name once more than one is registered)
* followed by the shared selection colour.
* every switched-on provider lacks its key, and the legend lists one swatch
* per active source, in its colour and under its name, followed by the
* shared selection colour.
* @param {{enabled: boolean, filter: object, providers: Array<ProviderSnapshot>, street: object, sequence: object}} input
*/
export function composeUIState({
@@ -52,24 +52,18 @@ export function composeUIState({
}) {
const active = providers.filter((p) => p.on);
const { keyRequired, ...coverage } = summarizeCoverage(providers);
const legend = [];
for (const provider of active)
for (const entry of provider.legend)
legend.push({
key: `${provider.id}:${entry.key}`,
label:
providers.length > 1
? `${provider.name} ${entry.label}`
: entry.label,
color: entry.color,
});
const legend = active.map((provider) => ({
key: provider.id,
label: provider.name,
color: provider.color,
}));
if (active.length)
legend.push({ key: 'selected', label: 'Selected', color: COLORS.selected });
return {
enabled,
keyRequired,
filter: { ...filter },
providers: providers.map((p) => ({ ...p, legend: [...p.legend] })),
providers: providers.map((p) => ({ ...p })),
coverage,
legend,
sequence: { ...sequence },