Merge origin/main: Google Street View provider, translated

Main added the Google Street View street-level provider and key guidance
that offers another source. Its new text goes through t()/msg() with ru
and es entries; the key guidance the panel shows is built in the UI
language by src/ui/keyGuidance.js, while keySetupCore.mjs keeps the
English sentences the server and AI tools use.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-13 20:34:43 +04:00
commit 8b0bb031b6
63 files changed
+4097 -210

No files matched your search

+41 -8
View File
@@ -16,6 +16,8 @@ import { msg, t } from '../../i18n/index.js';
* @property {string} hint
* @property {string|null} error
* @property {string} color The source's one colour.
* @property {boolean} [groundClick]
* @property {Array<'pano'|'since'>} [filters]
*/
/**
@@ -31,7 +33,7 @@ export function summarizeCoverage(providers) {
hint:
providers.length === 0
? msg('No street-level imagery providers configured')
: active.find((p) => p.hint)?.hint || '',
: [...new Set(active.map((p) => p.hint).filter(Boolean))].join(' · '),
error: active.find((p) => p.error)?.error || null,
keyRequired:
active.length > 0 && active.every((p) => p.keyRequired === true),
@@ -42,13 +44,39 @@ export function summarizeCoverage(providers) {
};
}
/**
* Switched-off providers that would draw with a key already set, offered
* while the switched-on ones lack theirs.
* @param {Array<ProviderSnapshot>} providers
* @returns {Array<{id: string, label: string, requiresKeyId: string|null}>}
*/
export function keyAlternatives(providers) {
if (!summarizeCoverage(providers).keyRequired) return [];
return providers
.filter((p) => !p.on && p.configured === true && p.keyRequired !== true)
.map(({ id, label, requiresKeyId }) => ({ id, label, requiresKeyId }));
}
/**
* The filter controls a switched-on provider honours (both when none is on).
* @param {Array<ProviderSnapshot>} providers
*/
export function filterControls(providers) {
const active = providers.filter((p) => p.on);
const honoured = (key) =>
active.length === 0 ||
active.some((p) => (p.filters ?? ['pano', 'since']).includes(key));
return { pano: honoured('pano'), since: honoured('since') };
}
/**
* The panel snapshot from core state and provider snapshots. The legend has
* one swatch per active source, then the shared selection colour.
* one swatch per active source that draws coverage lines, then Selected.
* @param {{enabled: boolean, filter: object, providers: Array<ProviderSnapshot>, street: object, sequence: object}} input
*/
export function composeUIState({
enabled,
groundClickReady = false,
filter,
providers,
street,
@@ -58,12 +86,14 @@ export function composeUIState({
const active = providers.filter((p) => p.on);
const { keyRequired, keyRejected, ...coverage } =
summarizeCoverage(providers);
const legend = active.map((provider) => ({
key: provider.id,
label: provider.name,
color: provider.color,
}));
if (active.length)
const legend = active
.filter((provider) => provider.groundClick !== true)
.map((provider) => ({
key: provider.id,
label: provider.name,
color: provider.color,
}));
if (legend.length)
legend.push({
key: 'selected',
label: t('Selected'),
@@ -71,9 +101,12 @@ export function composeUIState({
});
return {
enabled,
groundClickReady: groundClickReady === true,
keyRequired,
keyAlternatives: keyAlternatives(providers),
keyRejected,
filter: { ...filter },
filterControls: filterControls(providers),
providers: providers.map((p) => ({ ...p })),
coverage,
legend,