refactor(street-level): one provider, no orbit overview, no prewarm, simpler tile choice

The layer now owns its Mapillary parts directly: the provider registry,
pick router, multi-provider UI summary, params codec helper and provider
context are gone. createStreetLevelLayer({ source }) builds coverage,
sequences, the nearest-image lookup and the MapillaryJS viewer itself; a
single providerOn switch keeps the share link's `mapillary` option (off
draws nothing), and getParams/setParams keep { mapillary, pano, sinceDays }.

No z0-5 overview points: above 60 km nothing is drawn and the panel says to
zoom in; the proxy now serves only coverage z11-14. Tiles come from the
visible ground within range of the camera, ranked from the ground under it
(no line-of-sight ranking or near-range padding). MapillaryJS loads on the
first photo; viewer prewarm is removed. Key status, a rejected key and a
rate limit share one `blocked` state on coverage.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-07 22:19:24 +04:00
1 parent 0f41fb04cd
commit 37a96072fa
51 files changed
+1240 -2659

No files matched your search

+6 -11
View File
@@ -4,7 +4,7 @@ import test from 'node:test';
import { createApplicationStreetLevel } from '../app/layers/streetLevel.js';
import {
fixtureTile,
EXPECTED_PROVIDERS,
LEGEND_SWATCHES,
isCollapsed,
PARK,
STRICT_ALLOWED_SKIPS,
@@ -25,13 +25,13 @@ import { redact } from '../../scripts/qa-browserEvidence.mjs';
globalThis.requestAnimationFrame ??= (callback) =>
setTimeout(() => callback(Date.now()), 0);
test('the harness expects exactly the providers the app registers, in order', () => {
// Any method a provider asks its source for is a no-op: only ids matter.
test('the harness expects the legend the app layer draws', () => {
// Any method the layer asks its source for is a no-op: only the legend matters.
const source = new Proxy({}, { get: () => () => {} });
const layer = createApplicationStreetLevel({
sources: { mapillary: source },
});
assert.deepEqual([...EXPECTED_PROVIDERS], [...layer.providerIds]);
assert.equal(layer.getUIState().legend.length, LEGEND_SWATCHES);
});
test('a panel reads as collapsed only with the collapsed class', () => {
@@ -68,13 +68,8 @@ test('fixture tiles give the hermetic gate something real to filter', async () =
const year = 365 * 86_400_000;
const old = grid.filter((s) => now - s.capturedAt > year).length;
assert.ok(old > 0 && old < 8, 'recent and older both present');
const orbit = decodeCoverageTile(fixtureTile(3, 1, 3, now), {
x: 1,
y: 3,
z: 3,
});
assert.ok(orbit.overview.length > 0, 'overview points from orbit');
assert.equal(fixtureTile(8, 1, 1, now).length, 0, 'nothing in between');
assert.equal(fixtureTile(3, 1, 3, now).length, 0, 'nothing from orbit');
assert.equal(fixtureTile(8, 1, 1, now).length, 0, 'nor in between');
});
/* ── The real status route probe ─────────────────────────────────────── */