npm run i18n now also lists, and fails on, markup text and title /
aria-label / placeholder without data-i18n*, and string literals that reach
the page (textContent, innerHTML, setAttribute, toasts, label:/title:
properties and similar) without t(), tn() or msg(). Opt-outs say why where
the text lives: translate="no" and data-i18n-runtime in markup,
// i18n-ignore in code. src/tools/ is skipped: its text is for AI clients.
534 places are reported today; npm test does not run this part yet.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Register es next to ru (selector shows EN | ES | RU) and complete the
catalog against the current English: the 13 messages added since #602,
the "many" plural form Intl.PluralRules('es') selects for exact millions,
and fixes where the English had changed (e.g. "hace hace" in LIVE ·
updated, minutes written as metres). Terminology made consistent (buque,
aeronave, incendio activo); key names stay in English. Meir's wording is
kept everywhere else. Adds docs/translations/es.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Conflicts with upstream's Street Level provider composition and share-state
codec, resolved:
- Street View's switch moves from its own share option (`g`) to upstream's
provider-switch field (`r`, e.g. `0.r.google-1`); a link without it keeps
Street View off. Docs and the browser gate follow.
- createApplicationStreetLevel keeps upstream's injectable provider list;
its default, once the application supplies its Street Level source, is
Mapillary plus Google Street View. Without that source there are none, as
upstream intends.
- The coverage hint keeps upstream's empty-composition message and joins
every switched-on provider's hint otherwise.
- CHANGELOG, README and SECURITY keep both sides' entries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Replace the semantic keys with the English source text, so code reads as the
UI does and English needs no catalog: t('Save keys'), tn(count, one, other)
for plurals, msg() for text stored in data, and bare data-i18n* markup that
translates the element's own English. Other languages are one flat JSON each.
- Simplify src/i18n: drop namespaces, the configurable locale pair
(GEV_DEFAULT_LOCALE / GEV_SECONDARY_LOCALE) and its build defines. Locale
comes from ?lang=, the stored choice, then browser languages.
- Add scripts/i18n.mjs (npm run i18n): extracts every message from t/tn/msg
and markup, and fails on missing, unused, placeholder-mismatched or
wrongly-shaped plural entries. npm test runs it via catalog.test.mjs.
- Add Russian (src/i18n/locales/ru.json, 833 messages), started from the
community catalog in #312 and reviewed against the current English.
- Fix data-i18n placed on parents that would wipe their children (cockpit
LEVEL and local weather cells, first-run EXPLORE MANUALLY tile), and a
doubled degree sign in the TRACK ACQUIRED signal.
- Rewrite docs/TRANSLATORS.md for the new workflow; drop sections the earlier
merge resurrected in docs/CURRENT-STATE.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A KMZ could name one large packed image in thousands of <href>s, and each
one made a fresh Blob copy for Cesium to decode. kmzImageResolver now keeps
one blob URL per archive entry, and countKmlGeometry refuses a document
with more than 2,000 <href> and <Style> elements (MAX_KML_LINKS) before
Cesium reads it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review of #966: with a Google Maps key but no Mapillary token, switching
Street Level on read KEY REQUIRED / "Needs MAPILLARY_CLIENT_TOKEN" with
nothing about Street View, which stayed off (billing), so the layer looked
broken.
- While the switched-on providers lack their keys, the layer reports the
switched-off ones that would draw with a key already set
(keyAlternatives: Street View on Google 3D with a Google key). The
panel's KEY REQUIRED tooltip, its coverage line and the layer row's
tooltip then read "Needs MAPILLARY_CLIENT_TOKEN — add it in Provider
Settings, or light STREET VIEW to use your Google Maps key"
(keySetupWithAlternatives). Off Google 3D the plain KEY REQUIRED stays.
Street View stays off by default.
- OPEN STREET VIEW moves beside the coverage hint: at the default panel
width the PROVIDERS row has no room for it, so it wrapped onto a line of
its own.
- The panel gate's corner drag names what lay under the pointer when it
fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer follow-up to #966 review:
- A failed panorama-details lookup now waits DETAILS_RETRY_COOLDOWN_MS
(5 s) per panorama before a view event can retry it, so dragging the
view after a failure no longer issues a lookup on every pov change. A
success clears the pause; one panorama's failure does not delay another.
- SECURITY.md notes that allowing maps.googleapis.com in script-src also
trusts its JSONP endpoints, an accepted limitation alongside the
existing 'unsafe-eval' allowance.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- OPEN STREET VIEW in the PROVIDERS row opens Street View's nearest
panorama at the ground under the middle of the screen, without moving
the camera: Street View no longer needs a pointer. It is disabled (and
says so) until the camera is at street zoom.
- Off the Google 3D map Street View does not appear at all: no chip,
legend swatch, globe credit, layer source or button, and Google's script
is not loaded (the viewer has no prewarm; the script loads on first use).
The share option keeps the user's choice for when Google 3D returns.
- A ground click measures the camera height at the click rather than
reading the cached flag.
- A chip without a usable key explains it instead of lighting; it darkens
beside a lit chip, and the other chips keep switching, so a refused
Street View key cannot lock Mapillary out.
- The gate checks that nothing of Street View shows off Google 3D even when
a share link switches it on, presses OPEN STREET VIEW on Google 3D, and
asserts that each chip it switches really switched.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Google's Maps Platform terms forbid Street View imagery beside non-Google
maps, so the provider declares requiresMapStack: 'photoreal': off Google
3D its chip is dashed with the reason, lookups and opens skip it, and
leaving Google 3D closes its panorama.
- Ground clicks: only a click on the map itself (scenePick.isSurfacePick:
nothing picked or Google 3D tile content) at street zoom opens Street
View, and the camera stays put; higher up the hint says to zoom in.
scenePick.pickGroundPosition replaces the copy in Directions.
- Viewer: poses go out at once and again when the date and address
arrive, so an open never hangs on that lookup; one lookup per panorama,
seeded by the nearest lookup; a close settles a pending open; failed
lookups are asked again; poses follow position changes, not pano_changed.
- Loader: the gm_authFailure handler is installed once, so a retried load
reports a refusal once.
- Viewer host: a prewarm that lands after another provider took the host
is released; FIT/FILL availability is set on every open.
- Provider: a refusal while switched off reports no error over another
photo; SINCE compares against the end of Google's month; messages name
the referrer and network causes; FLAT says Street View is 360° only.
- The layer row names the key of the providers that are on; the MCP panel
CSP admits Street View's tiles, control images and photos.
- Tests for each, against fakes that now follow Google's event order; the
browser gate restores its state, waits instead of sleeping, aims its
panel clicks and corner drags, and asserts the camera stays put.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Google Street View registers beside Mapillary as provider `google`. It runs
on the Maps JavaScript API with the browser GOOGLE_MAPS_API_KEY, loaded on
first use (mapsLoader.js), and uses Google's own StreetViewPanorama as its
viewer adapter, emitting provider-neutral poses (address and month in the
caption, a link to Google Maps, steps along Google's arrows, FOLLOW on
Google 3D). Its chip starts off because every panorama is billed to the
key; the share option is `g`.
Google offers no coverage layer for other maps, so the provider draws
none: with it on, a click on bare ground at street zoom opens its nearest
Google-collected outdoor panorama (groundClick.js). A key Google refuses
reads KEY REJECTED and fails a pending open at once; a lookup Google never
answers fails after 10 s.
Core: providers may declare `groundClick` and `defaultOn`; poses may carry
`capturedAtPrecision` and `title`; the coverage line keeps every
provider's hint; opening one provider's image releases another's
prewarmed viewer; FIT/FILL hides for viewers without render modes. CSP
script-src admits https://maps.googleapis.com. The browser gate gains two
Street View steps (the keyed one skips as 'no Google key' in CI).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Imported metadata stays inert: descriptions, native labels, properties and
KML extended data are dropped from every entity; names are kept as short
plain text. GeoJSON loads with a describe that builds no HTML, and
simplestyle (including marker-symbol) is dropped, so no icon asset is
fetched for a file and GeoJSON draws in its row's color.
- Bound geometry before loading: features and vertices are counted in the
parsed GeoJSON, the KML document and while reading GPX, and a file over
20,000 features or 250,000 vertices is refused before Cesium builds it.
- Imports run one batch at a time, so concurrent picks or drops cannot pass
the 12-file cap.
- Fly-to framing walks long tracks by index instead of spreading them into a
call.
- Fly-to is an immediate navigation through the application shell; the fly
after an import is a deferred navigation stamped when the batch starts and
re-asserted just before flying.
- Dated imports never change the shared clock: imported sources expose no
clock, entities are available for all time, and a KML gx:Track is drawn as
its line.
- GPX is read into GeoJSON with one line per track segment, without times.
- KML/KMZ rows show a hatched swatch (they keep their own styles); keyboard
focus is kept on the same control after hide/show and moves to the next
row after remove.
- Unit regressions for each, and the rendered QA now imports hostile
metadata, dated KML/GPX and a two-segment track, and fails on any request
for a file's contents or icon assets, a clock change, or surviving
metadata.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
viewer.flyTo frames ground-clamped billboards before the destination's
terrain has loaded, and their bounding sphere then sits kilometers below
the surface: flying to a points-only GeoJSON from another city left the
camera about 2.3 km underground (a black screen). Frame the features'
longitudes and latitudes at the surface instead, and let the existing
camera ground guard lift the eye once the terrain can be measured
(src/ui/flyToDataSource.js, with tests).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
DISPLAY ▸ Import (and drop onto the globe) puts the person's own files on
the map as Cesium data sources, clamped to the ground, one color per file,
with a row to show/hide, fly to or remove each. Files stay in the browser
for the session.
KML/KMZ are sanitized before Cesium reads them (network links, remote
icons/styles, models, photo/screen overlays and balloon HTML removed; KMZ
images served from blob URLs), so an imported file cannot make the page
fetch anything. KMZ is read with a small dependency-free zip reader on the
platform DecompressionStream, with a zip-bomb limit. Bounded file size,
entity count and number of files.
Tests: src/data/geoFileImport.test.mjs, src/ui/geoFileImport.test.mjs.
Gate: scripts/qa-geo-import.mjs. The import half of #890.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- A photo takes a deferred navigation ticket when it starts opening
(begin/reassert, the application's _beginDeferredNavigation and
_reassertNavigationHandoff) and frames only if that ticket still owns
the camera once loaded, so a slow photo no longer flies the camera back
over newer navigation or clears newer aircraft tracking.
- A failed mesh refresh keeps the last good sample and the distance it was
taken from, and is retried after the miss cooldown, instead of recording
the failed distance and never refreshing again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>