Merge branch 'main' into fix/alpha5-norad-id

# Conflicts:
#	src/layers/satellites/ingestion.js
This commit is contained in:
Sameh Khamis committed 2026-10-17 11:36:21 -07:00
commit 6ae854c121
543 files changed
+42557 -4887

No files matched your search

+16 -7
View File
@@ -4,6 +4,7 @@ import {
satelliteClassLegend,
} from '../../data/satelliteClass.js';
import * as Cesium from 'cesium';
import { t } from '../../i18n/index.js';
import { ISS_NORAD, POINT_STYLES } from './policy.js';
export function createControls({ state: layerState, services, parts, source }) {
@@ -493,19 +494,27 @@ export function createControls({ state: layerState, services, parts, source }) {
const active =
layerState._params.catalog === 'dense' &&
layerState._denseStatus === 'ready';
let title =
'Add the full Starlink broadband shell (thousands of extra points)';
if (loading) title = 'Loading the Starlink shell…';
let title = t(
'Add the full Starlink broadband shell (thousands of extra points)',
);
if (loading) title = t('Loading the Starlink shell…');
else if (failed)
title = `Starlink ${layerState._denseError || 'load failed'} — click to retry`;
title = t('Starlink {detail} — click to retry', {
detail: layerState._denseError || t('load failed'),
});
else if (active)
title =
'Showing the full Starlink shell — click for the core catalog only';
title = t(
'Showing the full Starlink shell — click for the core catalog only',
);
return {
chips: [
{
id: 'catalog',
label: loading ? 'DENSE ···' : failed ? 'DENSE ✕' : 'DENSE',
label: loading
? t('DENSE ···')
: failed
? t('DENSE ✕')
: t('DENSE'),
active,
busy: loading,
disabled: loading,
+4 -1
View File
@@ -1,5 +1,6 @@
import { twoline2satrec } from 'satellite.js';
import * as Cesium from 'cesium';
import { msg } from '../../i18n/index.js';
import { noradIdFromSatnum } from '../../sources/tle.js';
import { CATALOG_GROUPS, ISS_NORAD, POINT_STYLES } from './policy.js';
@@ -60,7 +61,8 @@ export function createIngestion({
// the chip still read "just now". Keep the existing (stale) catalog on
// screen and surface the outage instead — do NOT stamp _lastUpdate.
if (results.every((r) => !r.ok)) {
layerState._lastError = 'CelesTrak unreachable';
// English data (stats reach the voice tools); the panel translates it.
layerState._lastError = msg('CelesTrak unreachable');
console.warn(
'[Data:Satellites] All CelesTrak groups failed — keeping existing catalog, surfacing outage',
);
@@ -71,6 +73,7 @@ export function createIngestion({
// At least one group loaded — proceed with a fresh rebuild, but keep the
// partial outage visible at the layer control instead of presenting the
// reduced catalog as a fully healthy refresh.
// i18n-ignore: English data; the panel shows it through FEED_ERROR_PATTERNS (ui/statusText.js)
layerState._lastError = failed.length
? `${failed.length} CelesTrak group${failed.length === 1 ? '' : 's'} unavailable`
: null;
+1 -1
View File
@@ -14,7 +14,7 @@ export function createLabels({ state: layerState, services, parts, source }) {
id: String(ISS_NORAD),
position,
variant: 'label',
title: 'ISS',
title: 'ISS', // i18n-ignore: satellite name, as in the satellite catalog
accent: '#ff4444',
priority: 1000,
collisionGroup: 'ambient-label',
+3 -1
View File
@@ -37,7 +37,9 @@ export function mapAnalystRecord(raw) {
// Empty records stay class-less; the class table's fallback is VISUAL and
// must not leak onto a row that has no satellite identity.
satelliteClass:
noradId || group ? satelliteClassLabel(group, { isIss }) : null,
noradId || group
? satelliteClassLabel(group, { isIss, localize: false })
: null,
group,
};
}
+15 -3
View File
@@ -1,5 +1,7 @@
import * as Cesium from 'cesium';
import { satelliteClassLabel } from '../../data/satelliteClass.js';
import { t } from '../../i18n/index.js';
import { formatQuantity, unitLabel } from '../../ui/unitLabels.js';
import {
ISS_NORAD,
CONTEXT_REFRESH_INTERVAL_MS,
@@ -239,7 +241,10 @@ export function createTracking({ state: layerState, services, parts, source }) {
name,
operator: '',
noradId: String(noradId),
class: satelliteClassLabel(sat.group, { isIss: noradId === ISS_NORAD }),
class: satelliteClassLabel(sat.group, {
isIss: noradId === ISS_NORAD,
localize: false,
}),
altitude:
altitudeKm === null ? '' : `${altitudeKm.toLocaleString('en-US')} km`,
},
@@ -351,7 +356,12 @@ export function createTracking({ state: layerState, services, parts, source }) {
const title = sat?.name?.trim() || `SAT-${layerState._trackedNorad}`;
const altitudeM =
layerState._trackedFrameGeo?.altitude ?? fallbackAltitudeM;
const detail = `${Number.isFinite(altitudeM) ? Math.round(altitudeM / 1000) : '?'} km · NORAD ${layerState._trackedNorad}`;
// The card is display only (the voice context slot keeps its own English
// copy), so the altitude unit and the companions line follow the language.
const altitudeText = Number.isFinite(altitudeM)
? formatQuantity(Math.round(altitudeM / 1000), 'km')
: `?\u00a0${unitLabel('km')}`;
const detail = `${altitudeText} · NORAD ${layerState._trackedNorad}`;
// Class leads the detail block: it is what tells the operator WHAT they are
// looking at, and it stays readable under the IR styles that flatten the
// dot colors to a single channel (the card is painted above post-FX).
@@ -369,7 +379,9 @@ export function createTracking({ state: layerState, services, parts, source }) {
if (companions.length > 0) {
const extra = companions.length - 1;
details.push(
`DOCKED · ${companions[0]}${extra > 0 ? ` · +${extra}` : ''}`,
extra > 0
? t('DOCKED · {name} · +{extra}', { name: companions[0], extra })
: t('DOCKED · {name}', { name: companions[0] }),
);
}
const current = layerState._trackedEntity.gevLabelModel;