Merge origin/main: keep the provider registry, slim it

main grew the Street Level provider registry while this branch removed it
(#982–#985: injected providers, an empty composition, the `r` switch field).
The registry, params, uiState and pick router come back as main has them and
the slimmed layer sits behind the provider contract:

- The core (index.js) drives providers as on main, without surface modes,
  ground casting, FOLLOW, prewarm and nearest-image lookups. A provider's key
  status is asked with a 10 s timeout and asked again on a switch-on while no
  key is known.
- Mapillary is mapillaryProvider.js over the slim coverage, sequences and
  viewer modules, which read the filter and the active flag from the provider
  context.
- The viewer host mounts one provider adapter at a time, no prewarm.
- The panel has a chip per provider again (the pill switches the layer);
  no legend, EXPAND stays the Fullscreen API.
- The contract drops what no provider implements: groundCaster, meshSampler,
  getSurface/setSurface, nearestImage, prewarm, reportError, the legend and
  surface fields of the UI snapshot.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Fable 5.1 committed 2026-10-13 13:24:25 +04:00
commit c29ccd55d6
103 files changed
+5469 -522

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
/**
* @typedef {object} ProviderSnapshot
* @property {string} id
* @property {string} name
* @property {string} label
* @property {boolean} on
* @property {boolean|null} configured null until the status call answers
* @property {boolean} keyRequired no usable key: missing or rejected
* @property {boolean} [keyRejected] the provider refused the configured key
* @property {string|null} requiresKeyId
* @property {boolean} loading
* @property {number} count
* @property {string} hint
* @property {string|null} error
* @property {string} color The source's one colour.
*/
/**
* Totals over switched-on providers. Key-gated only when every one lacks its
* key; `keyRejected` when that is because a key was refused.
* @param {Array<ProviderSnapshot>} providers
*/
export function summarizeCoverage(providers) {
const active = providers.filter((p) => p.on);
return {
count: active.reduce((sum, p) => sum + (p.count || 0), 0),
loading: active.some((p) => p.loading),
hint:
providers.length === 0
? 'No street-level imagery providers configured'
: active.find((p) => p.hint)?.hint || '',
error: active.find((p) => p.error)?.error || null,
keyRequired:
active.length > 0 && active.every((p) => p.keyRequired === true),
keyRejected:
active.length > 0 &&
active.every((p) => p.keyRequired === true) &&
active.some((p) => p.keyRejected === true),
};
}
/**
* The panel snapshot from core state and provider snapshots.
* @param {{enabled: boolean, filter: object, providers: Array<ProviderSnapshot>, street: object, sequence: object}} input
*/
export function composeUIState({
enabled,
filter,
providers,
street,
sequence,
}) {
const { keyRequired, keyRejected, ...coverage } =
summarizeCoverage(providers);
return {
enabled,
keyRequired,
keyRejected,
filter: { ...filter },
providers: providers.map((p) => ({ ...p })),
coverage,
sequence: { ...sequence },
street: { ...street },
};
}