Tighten coverage prompt comments and docs

This commit is contained in:
German Khokhlov committed 2026-11-06 20:20:11 +04:00
1 parent 3a78f40024
commit 5c4918affb
12 files changed
+31 -54

No files matched your search

+2 -3
View File
@@ -2393,9 +2393,8 @@ export class LayerLifecycle {
}
/**
* A tick's failure belongs to the feed it polled. Once the layer's own stats
* stop reporting one (the camera left that feed), the row stops showing it
* instead of waiting for the next tick.
* Drop a tick failure the layer's stats no longer report (the camera left
* that feed) instead of showing it until the next tick.
*/
_dropRecoveredRefreshError(layerId) {
const entry = layerId ? this.layers.get(layerId) : null;
+5 -8
View File
@@ -15,8 +15,7 @@ export function createControls({ state: layerState, services, parts, source }) {
updateInterval: STATUS_POLL_MS,
/**
* Keep the manager so camera-driven coverage changes can repaint the row
* before the next status poll.
* Kept so camera moves can repaint the row before the next status poll.
* @param {object} dataManager DataLayerManager instance.
*/
attachDataManager(dataManager) {
@@ -84,18 +83,16 @@ export function createControls({ state: layerState, services, parts, source }) {
lastUpdate: layerState._lastUpdate,
loading,
};
// No system in range is guidance, not an empty feed: the camera is too
// high, or no catalogued system is near the view. A city that failed
// leaves range empty too, and reports its failure below instead.
// No system in range: the camera is too high or no system is near. A
// failed city also leaves range empty and reports its failure below.
if (
layerState._enabled &&
!loading &&
layerState._activeCityIds.size === 0 &&
layerState._failedCityIds.size === 0
) {
// English data (the analyst's empty note to the model, and the Layers
// row's prompt); both show it through translateStatusMessage
// (ui/statusText.js).
// English: the analyst's empty note and the row's prompt, both shown
// through translateStatusMessage (ui/statusText.js).
const note = layerState._altitudeGateEnabled
? `No bike share here yet · ${GBFS_CITY_REGISTRY.length} systems available`
: `Fly below ${Math.round(ACTIVATION_ENTER_ALTITUDE_M / 1000).toLocaleString('en-US')} km to a covered city`;
+2 -5
View File
@@ -1,10 +1,7 @@
// BIKESHARE COVERAGE STATUS — a view with no system says so.
//
// Over a city with no catalogued system (London, Tokyo) the layer drew nothing
// and reported nothing, so its row read "ON · — · GBFS · never": the same as a
// feed that had never answered. These cases pin the rule that replaced it:
// with no system in range the layer reports a guidance status that says why,
// and a view it left keeps no date for data it no longer draws.
// With no system in range (London, Tokyo) the layer says why instead of
// "never", and keeps no date for data it no longer draws.
//
// Run with: npm test
import { test } from 'node:test';
-2
View File
@@ -1,5 +1,3 @@
// Bike Share row repaints: entering a city, leaving it or nudging the camera
// mid-load, and leaving a failed city.
// Bike Share: after the camera settles, the last row the panel painted must
// match what the layer reports now. Each case drives the real viewport,
// ingestion, rendering and controls; the manager is a spy that records the
+1 -4
View File
@@ -116,10 +116,7 @@ export function createViewport({ state: layerState, services, parts, source }) {
parts.rendering.removeCityPoints(cityId);
}
/**
* Repaint the layer's row now. Its text follows the camera (coverage
* prompt, station count), and the manager tick is 60 s.
*/
/** Repaint the row now: it follows the camera, the manager tick is 60 s. */
function refreshRow() {
layerState._dataManager?.refreshLayerStats?.('bikeshare');
}
+2 -4
View File
@@ -1,7 +1,5 @@
// When the camera changes which coverage prompt a Transit or
// Bike Share row shows, the layer asks the panel to repaint at once, instead
// of leaving the old prompt up until the manager's next tick (Transit 15 s,
// Bike Share 60 s).
// A camera move that changes a Transit or Bike Share coverage prompt repaints
// the row at once, not at the next manager tick (15 s, 60 s).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createState as createBikeState } from './bikeshare/state.js';
+2 -4
View File
@@ -1,7 +1,5 @@
// The Transit coverage prompt names the altitude the closed gate
// actually opens at. Descending over Boston to 2,900 km (below 3,000 km, above
// the 2,850 km enter threshold) loads nothing, so a prompt "Fly below 3,000 km"
// asks for something the camera has already done.
// The Transit prompt names the altitude the gate opens at: at 2,900 km over
// Boston (under 3,000 km, above the 2,850 km enter threshold) nothing loads.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createState } from './state.js';
+2 -3
View File
@@ -245,9 +245,8 @@ export function createQueries({ state, parts }) {
const health = aggregateTransitFeedHealth(statuses, now);
const count = state._vehicles.size;
if (state._enabled && active.length === 0) {
// English data (the analyst's empty note to the model, and the Layers
// row's prompt); both show it through translateStatusMessage
// (ui/statusText.js).
// English: the analyst's empty note and the row's prompt, both shown
// through translateStatusMessage (ui/statusText.js).
const note = state._altitudeGateOpen
? `No feed here yet · ${TRANSIT_REGION_COUNT} regions available`
: `Fly below ${Math.round(ACTIVATION_ENTER_ALTITUDE_M / 1000).toLocaleString('en-US')} km to a covered region`;
+2 -4
View File
@@ -111,8 +111,7 @@ export function createViewport({ state, services, parts }) {
changed = true;
}
}
// Leaving coverage leaves nothing on the map to date: a row that read
// "26s ago" over an empty view would claim a feed that is no longer here.
// Nothing is left on the map to date.
if (desired.size === 0) state._lastUpdate = null;
for (const [feedId, feed] of desired) {
if (!state._activeFeeds.has(feedId)) {
@@ -124,8 +123,7 @@ export function createViewport({ state, services, parts }) {
parts.ingestion.sweepAgedVehicles(Date.now());
parts.rendering.syncRenderHold();
governorRequestRender('transit-proximity');
// The row's text follows the camera (coverage prompt, feed list), so it
// repaints now instead of at the next 15 s manager tick.
// Repaint now: the row follows the camera, the manager tick is 15 s.
if (changed) state._dataManager?.refreshLayerStats?.('transit');
}
+2 -4
View File
@@ -269,10 +269,8 @@ export function translateStatusLine(text) {
/** Thousands separators out of an English number ("3,000" → 3000). */
const englishNumber = (text) => Number(text.replace(/,/g, ''));
// Coverage notes of a layer whose view has no feed, whole lines with a number
// in them: transit (transit/queries.js) and bikeshare (bikeshare/controls.js).
// Their " · " is part of one sentence, so they are matched before the line is
// split into pieces. Keep these in step with the producers.
// Coverage notes (transit/queries.js, bikeshare/controls.js). Their " · " is
// inside one sentence, so they match whole before the line is split.
const COVERAGE_NOTE_PATTERNS = [
[
/^Fly below ([\d,]+) km to a covered region$/,