fix(street-level): sequence loads, playback and nearest-photo edge cases

From gekh's review of #768, each bug with a test that fails before:

- P2: a failed sequence load clears the previous sequence's cones, resets
  the selection and can be retried; a second click while loading starts no
  second lookup. sequences.js had no tests: sequences.test.mjs now covers
  failure and retry, a superseded late answer, the cache and clearSelection.
- P2: closing a photo stops MapillaryJS sequence playback, which kept the
  hidden viewer stepping and downloading images.
- openNearest without the panel reports it instead of loading forever, and
  stops if the layer (or its provider) went off during the lookup; the
  mesh sampler's misses are bounded and forgotten with its samples; the
  sequence count is the union of ids across tiles; a provider withdraws
  only its own error, so a sequence error cannot mask KEY REJECTED; hover
  stays on (throttled) under an orbiting camera and re-picks when it
  stops; "since N days" follows the clock in a long-open tab.
- A prewarm waits for one in flight and then builds if it gave up.
- New coverage for the layer core (credits, KEY REJECTED vs REQUIRED,
  switching a provider off), the refresh wiring of the tilted-view fix and
  the draped-to-cast swap completion.
- Cleanup: loading is derived from pending; freshStreet(); tile ranking
  computes distances once and shares one path for the date line; shared
  metresBetween / whenIdle / redrawTile; one NEAREST_RADIUS_M and one
  source method list; unused contract fields, exports, getImage and the
  decoder's image branch removed; PROVIDER_COLORS lists only Mapillary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-05 18:35:44 +01:00
1 parent 48276450ec
commit 5f98b871e8
31 files changed
+1129 -470

No files matched your search

+28 -17
View File
@@ -1,5 +1,32 @@
import { FILTER_DEFAULT } from './policy.js';
/**
* The open image's fields of `state.street`, as they are with no image open.
* Closing the viewer resets exactly these; the panel host, the render mode
* and whether following is available outlive any one image.
*/
export function freshStreet() {
return {
open: false,
follow: false,
providerId: null,
providerName: null,
providerLabel: null,
imageId: null,
position: null,
bearing: null,
tilt: null,
altitude: null,
isPano: false,
capturedAt: null,
sequenceId: null,
creator: null,
externalUrl: null,
loading: false,
error: null,
};
}
/** Mutable core state, created once per layer instance. */
export function createState({ services }) {
return {
@@ -17,27 +44,11 @@ export function createState({ services }) {
street: {
host: null,
open: false,
follow: false,
/** Whether the active map stack allows following (Google 3D only). */
followAvailable: false,
providerId: null,
providerName: null,
providerLabel: null,
imageId: null,
position: null,
bearing: null,
tilt: null,
altitude: null,
isPano: false,
capturedAt: null,
sequenceId: null,
creator: null,
externalUrl: null,
loading: false,
error: null,
/** 'letterbox' shows the whole image; 'fill' crops it to the frame. */
renderMode: 'letterbox',
...freshStreet(),
},
/** 'terrain' on Google 3D at street zoom (overlays on the bare earth), else 'draped'. */