Merge upstream/main: cockpit emergency, GNSS integrity and GeoJSON import meet i18n

Upstream advanced past the KartaView merge: transponder-emergency HUD
wording (formatCockpitEmergency in cockpitMath), the GNSS integrity
layer joined the Events group, and a GeoJSON/KML/KMZ/GPX file-import
control landed - all in regions this branch localizes.

- cockpitMath: upstream's formatCockpitEmergency kept alongside the
  branch's formatter-injected Context scope (module stays i18n-free;
  squawk/ADS-B codes and the broadcast label are data).
- cockpitInstruments: the emergency HUD line composed with t()
  placeholders; fleet/feedState stay translated, the 'emergency' class
  toggle kept.
- layerPanel: upstream's gnss-integrity id with msg('Events').
- package-boundaries: both sides' entries unioned.
- New upstream UI strings marked and translated, as the branch's
  unmarked-string detector requires: the geo-import control's markup
  (data-i18n-title/-aria-label/text), its list titles, hints and
  summarize() lines (t/tn with placeholders), the aircraft emergency
  labels (msg data), and the GNSS legend blurb (t with placeholders).
- es/ru catalogs: 26 new keys each (2019 messages).

npm run i18n: complete, no unmarked strings. npm test: 6739/6741 (the
one failure is the pre-existing environmental OPENAI_API_KEY leak in
devFreshDotenv). Build passes.
This commit is contained in:
Meir Michanie committed 2026-10-15 19:37:26 +02:00
commit 0211e6e877
120 files changed
+12921 -254

No files matched your search

+2 -2
View File
@@ -36,8 +36,8 @@ test('the complete Realtime tool payload pins the manifest-generated layer relea
// consolidated tool wording (each policy stated once); model-facing tools
// omit top-level anyOf (OpenAI rejects it). Contacts list routing preserves
// requested radius and center authority in descriptions only.
// Street Level adds its toggle enum values and generated alias hint.
'9eb113454c5af5e32e27ffcdc7919d0e4600ba1d204f8f175e071152be41ad0f',
// Street Level and GNSS extend the manifest; tracked and analyst aircraft describe broadcasts.
'9398815a573ab29ecc3ce527f6e1a2ad883804e7696e0444aebb11f973700d4b',
);
});
+6 -2
View File
@@ -4818,9 +4818,13 @@ function compactAnalystItem(r) {
for (const [key, spec] of Object.entries(fields)) {
if (!present(r[key]) || key in compact) continue;
if (spec.type === 'time') {
const ms = Number(r[key]);
// A finite value can still be past Date's range, where toISOString throws.
const time = new Date(Number(r[key]));
if (Number.isFinite(time.getTime())) compact.time = time.toISOString();
const time = new Date(ms);
if (Number.isFinite(time.getTime())) {
if (key === 'lastContactAtMs') compact.lastContactAtMs = ms;
else compact.time = time.toISOString();
}
} else compact[key] = r[key];
}
if (present(r.ageHours)) compact.ageHours = r.ageHours;
+27 -3
View File
@@ -3180,7 +3180,7 @@ test('voice Radio: a key that geocodes to nothing still places the station, keyl
const requests = [];
installKeyedFetch(t, async (url) => {
requests.push(String(url));
if (String(url).startsWith('https://maps.googleapis.com/')) {
if (String(url).startsWith('/api/google/geocode')) {
return { ok: true, json: async () => ({ status: 'ZERO_RESULTS', results: [] }) };
}
assert.match(String(url), /^https:\/\/photon\.komoot\.io\/api\/\?/);
@@ -3205,7 +3205,7 @@ test('voice Radio: a key that geocodes to nothing still places the station, keyl
assert.ok(Math.abs(calls[0].criteria.anchor.lat - 20.9101) < 1e-9);
assert.ok(Math.abs(calls[0].criteria.anchor.lon - 107.1839) < 1e-9);
// Google is asked first and exactly once; Photon answers unbiased, in one call.
assert.equal(requests.filter((url) => url.includes('maps.googleapis.com')).length, 1);
assert.equal(requests.filter((url) => url.includes('/api/google/geocode')).length, 1);
assert.equal(requests.filter((url) => url.includes('photon.komoot.io')).length, 1);
assert.match(requests.at(-1), /[?&]q=H%E1%BA%A1\+Long\+Bay/);
assert.doesNotMatch(requests.at(-1), /[?&](lat|lon|bbox)=/, 'a named radio location is not viewport-biased');
@@ -3218,7 +3218,7 @@ test('voice Radio: the keyless path applies no country filter the keyed path wou
// "No Radio station matched" for exactly the places it just resolved, while a
// keyed install placed a station. The label may carry it; the filter may not.
const { calls, dataManager } = radioSelectionHarness();
installKeyedFetch(t, async (url) => (String(url).startsWith('https://maps.googleapis.com/')
installKeyedFetch(t, async (url) => (String(url).startsWith('/api/google/geocode')
? { ok: true, json: async () => ({ status: 'ZERO_RESULTS', results: [] }) }
: {
ok: true,
@@ -3263,6 +3263,30 @@ test('ALPR common names toggle only the registered camera layer through the norm
}
});
test('GNSS integrity and interference common names toggle only the GNSS layer through the normal voice action', async () => {
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
const viewer = { clock: { onTick: { addEventListener: () => () => {} } },
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
camera: { moveEnd: { addEventListener() {} } } };
const calls = [];
let enabled = false;
const dataManager = {
layers: new Map([['gnss-integrity', { module: {} }]]),
getAll: () => [{ id: 'gnss-integrity', name: 'GNSS Integrity' }],
isEnabled: () => enabled,
setEnabled: async (id, value) => { calls.push([id, value]); enabled = value; return true; },
};
const runner = createGevActionRunner({ viewer, styleManager: {}, dataManager });
for (const alias of ['gnss-integrity', 'gnss integrity', 'navigation integrity', 'low navigation accuracy', 'gnss interference', 'gps interference', 'gps jamming', 'gnss']) {
for (const value of [true, false]) {
const result = await runner('set_layer_visibility', { layerId: alias, enabled: value });
assert.equal(result.ok, true);
assert.equal(result.layerId, 'gnss-integrity');
assert.deepEqual(calls.at(-1), ['gnss-integrity', value]);
}
}
});
test('Local ADS-B common names toggle only the receiver layer through the normal voice action', async () => {
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
const viewer = { clock: { onTick: { addEventListener: () => () => {} } },
+20
View File
@@ -34,9 +34,29 @@ const AIRCRAFT_FIELDS = {
aircraftClass: ['t'],
military: ['f'],
onGround: ['f'],
squawk: ['t'],
emergency: ['f'],
emergencyKind: ['t'],
emergencySource: ['t'],
emergencyBroadcast: ['t'],
lastContactAtMs: ['ms'],
};
const layers = [
{
id: 'gnss-integrity',
aliases: [
'gnss integrity',
'navigation integrity',
'low navigation accuracy',
'gnss interference',
'gps interference',
'gps jamming',
'gnss',
],
context: false,
noQuery: 'navigation-integrity cells; no countable analyst records',
},
{
id: 'flights',
track: true,