Merge branch 'main' into pr/mapillary-layer

This commit is contained in:
Said Turksever authored and GitHub committed 2026-09-29 15:44:25 +01:00
commit c4514784fc
201 files changed
+26665 -1973

No files matched your search

+1 -1
View File
@@ -58,7 +58,7 @@ export const COCKPIT_BRIEF_PAGES = [
id: 'local',
kicker: 'LOCAL INFO',
subtitle: 'PLACE / CONDITIONS / POSITION',
source: 'OPENSTREETMAP · OPEN-METEO · UTC',
source: 'NATURAL EARTH · OPEN-METEO · UTC',
},
];
+4
View File
@@ -115,6 +115,10 @@ export class LocationNavigation {
placeSearch: this.placeSearch,
...options,
}),
onResult: (destination) => {
if (destination.outlineUnavailable)
this._showToast('Detailed outline unavailable');
},
onError: (error) => console.error('[Search] Geocoding failed:', error),
});
this._locationLookupUnsubscribe = this._locationLookup.subscribe(
+3
View File
@@ -1,3 +1,4 @@
import { cancelCameraArrival } from '../data/cameraArrival.js';
import { createStateChannel } from '../app/stateChannel.js';
import * as Cesium from 'cesium';
import {
@@ -45,6 +46,7 @@ export class NavigationController {
cancelPendingSelection = true,
clearSearchedLocation = true,
} = {}) {
cancelCameraArrival(this.viewer);
this.cancelOrientation();
const { flightsLayer, militaryFlightsLayer, satellitesLayer } =
this.tracking;
@@ -257,6 +259,7 @@ export class NavigationController {
return this._cameraHandoffs.subscribe(listener, { emitCurrent: false });
}
stop() {
cancelCameraArrival(this.viewer);
this._disposed = true;
this._cameraHandoffs?.publish();
this._cameraHandoffs?.destroy();
+20 -4
View File
@@ -23,6 +23,8 @@ export class ShellFeedback {
null;
this._loadingVisibilityHandler = null;
this._lastLoadingFeedbackUpdateAt = 0;
this._lastTrafficChipUpdateAt = 0;
this._lastTrafficGlobalUpdateAt = 0;
this._globalLoadingStatus = document.getElementById(
'global-loading-status',
);
@@ -74,7 +76,10 @@ export class ShellFeedback {
// the flap keeps textContent equal to the settled label throughout, so
// this stays a no-op on the repeat ticks exactly as it did before.
if (presentation.label)
setSplitFlapText(this._trafficSyncLabel, presentation.label);
setSplitFlapText(this._trafficSyncLabel, presentation.label, {
// Completion is a timing signal, not another animated loading stage.
immediate: !presentation.busy,
});
// Written on every change INCLUDING the empty settled value — the reducer
// clears the progress number once the sync lands, and a truthiness guard
// here would strand the last "..." beside the settled label.
@@ -142,9 +147,20 @@ export class ShellFeedback {
if (this._trafficChipTicker) return;
this._trafficChipTicker = setInterval(() => {
if (document.hidden) return;
this._updateTrafficSyncChip();
this._updateGlobalLoadingFeedback();
}, 500);
const now = performance.now();
// Poll busy traffic promptly; retain the idle/global 500 ms read budget.
if (
this._trafficSyncFeedbackState.busy ||
now - this._lastTrafficChipUpdateAt >= 500
) {
this._lastTrafficChipUpdateAt = now;
this._updateTrafficSyncChip(false, now);
}
if (now - this._lastTrafficGlobalUpdateAt >= 500) {
this._lastTrafficGlobalUpdateAt = now;
this._updateGlobalLoadingFeedback(now);
}
}, 100);
}
_armLoadingFeedbackTicker() {
+38
View File
@@ -366,3 +366,41 @@ test('weather rail orders between CCTV and Context, resets positions, observes a
f.restore();
}
});
test('busy traffic completion is polled promptly without accelerating idle or global reads', (t) => {
const f = fixture();
let now = 500;
t.mock.method(performance, 'now', () => now);
try {
const owner = new ShellFeedback({ readLayers: () => [] });
let traffic = 0,
global = 0;
owner._updateTrafficSyncChip = () => traffic++;
owner._updateGlobalLoadingFeedback = () => global++;
owner._startTrafficChipTicker();
const tick = f.timers.get(owner._trafficChipTicker);
tick();
assert.equal(traffic, 1);
assert.equal(global, 1);
now = 600;
tick();
assert.equal(traffic, 1, 'idle polling remains bounded');
owner._trafficSyncFeedbackState.busy = true;
now = 700;
tick();
assert.equal(traffic, 2, 'busy completion is observed on a 100 ms tick');
assert.equal(global, 1, 'other loading feedback keeps its prior budget');
owner._trafficSyncFeedbackState.busy = false;
now = 800;
tick();
assert.equal(traffic, 2);
document.hidden = true;
now = 1300;
tick();
assert.equal(traffic, 2, 'hidden pages do no polling');
owner.destroy();
assert.equal(f.timers.size, 0);
} finally {
f.restore();
}
});
+3 -3
View File
@@ -442,17 +442,17 @@
letter-spacing: 0.035rem;
}
/* The compact dock leaves both lower corners free for required attribution and telemetry. */
/* Keep the complete inline attribution row above the command dock. */
body:not(.ui-clean-view):not(.recording-mode) #cesium-credits,
body:not(.ui-clean-view):not(.recording-mode):has(
#intel-hud[data-variant='minimal'].active
)
#cesium-credits {
right: auto;
bottom: 0.75rem;
bottom: calc(2vh + 5rem);
left: 1.5rem;
width: auto !important;
max-width: calc(50vw - 15rem);
max-width: calc(100vw - 3rem);
text-align: left;
}
-19
View File
@@ -321,25 +321,6 @@ body.cockpit-mode #cockpit-cloud-effects.active {
color: rgba(232, 234, 237, 0.7);
}
/* Keep the ALPR source overflow reachable even after its brief on-map credit
collapses. Wrap whole entries only while the optional layer is on. */
body:has(.data-toggle-row[data-layer-id='alpr-cameras'] .data-toggle-btn.active)
#cesium-credits {
white-space: normal;
}
body:has(.data-toggle-row[data-layer-id='alpr-cameras'] .data-toggle-btn.active)
#cesium-credits
.cesium-credit-wrapper {
display: inline-block !important;
white-space: nowrap;
}
body:has(.data-toggle-row[data-layer-id='alpr-cameras'] .data-toggle-btn.active)
#cesium-credits
.cesium-credit-expand-link {
/* Keep the complete link at the left edge, clear of the Power Up button. */
display: block !important;
}
/* Cesium's stock attribution lightbox grows with every registered data source.
Keep the title and close affordance fixed while the complete, unabridged
credit list wraps and scrolls inside a compact panel. */
+9
View File
@@ -337,6 +337,15 @@
#military-awareness-panel[hidden] {
display: none;
}
.military-awareness-names {
margin: 6px 0;
color: var(--text-dim);
}
.military-awareness-names > div {
max-height: 160px;
overflow-y: auto;
padding: 6px 0;
}
.military-awareness-subject {
margin: 0 0 7px;
color: #fff;