Main's work since the last merge runs on the globe contract where this
branch draws through it:
- Satellites take main's classes, sensors, ground-track bearings and
world time through contract points and rings; launches take the world
timeline. The satellite's cockpit subject dims, copies positions and
refocuses through the contract.
- Flights, military and vessels keep main's photos, retry cool-downs,
world-clock motion and the ship bridge. Trail seeding in the shared
aircraft tracking reads each layer's clock.
- Traffic's flow lines and single build draw as ground sets; transit
keeps dated credits and the new cities; CCTV opens the nearest camera,
and the Quebec overlay projects and tests the horizon through the
contract. Bikeshare's manager cleanup moved into its current lifecycle.
- Draw keeps main's mark actions across engine switches.
- The world clock in src/time is plain numbers again: Julian dates come
from src/geo, the frame loop is injected, and the binding to Cesium's
viewer clock lives in src/globe/cesium/worldTime.js. Navara's sky and
sun follow the world time; live feeds keep the real time.
- The cockpit's orbital view, with its sensor zoom, ground focus and
satellite switching, stays Cesium-only for now: Navara hides its entry.
Its Cesium calls live in src/globe/cesium/orbital.js. The ship bridge's
finest-detail waterline sampling is Cesium-only too; Navara samples
loaded geometry and detailed terrain. Both are in KNOWN-ISSUES.
- Main's tests that built a Cesium scene by hand run on the fake engine.
- The boundary manifest is regenerated, the coupling baseline records
the orbital and bridge code, and the main-chunk baseline is raised to
the merged size: main itself grew its main chunk by 93,959 bytes
(gzip -9) since the previous merge base.
Tracking a civil flight asks Planespotters for that aircraft's photo, once
per aircraft, straight from the browser. The CONTEXT panel's CONTACTS view
shows it under the flight's name with the photographer's credit, as a plain
link to the photo's page. Only images on plnspttrs.net and pages on
www.planespotters.net are ever shown or linked. Military aircraft and a
converted TR-3B are never looked up.
Flight lookups (type, route, photo) whose source failed temporarily are
asked again after a 60-second cool-down; an answer, found or not, and a
permanent refusal stay final for the session. A route belongs to the
callsign it was looked up for, so a callsign change drops the old route.
Browser QA scripts that track a flight answer the photo lookup themselves,
so they never call the third party.
With a satellite tracked and no aircraft tracked, Cockpit can ride the
satellite. Entry needs only the Satellites layer; debris can be tracked but
not entered, and voice stays aircraft-only. Double-clicking the tracked
satellite or the entry buttons (reading ORBIT) enter it.
The camera sits on the satellite's per-frame SGP4 position and looks
straight down, turned to the ground track (north-up for a satellite with
none). The optical zoom steps 1x to 8x with a focus pull, the arrows tilt
the view up to 45 degrees off nadir, and Home or 0 returns to nadir. Off
nadir the tilt reads its total angle first and the swath readout becomes an
approximation marked with ≈. While in orbit the map leases flat Esri
imagery, the cloud pass stays off and the view opens in the optical style.
The orbital HUD names the satellite with its owner's flag (from the
flag-icons package, loaded per country) or an organisation badge, its
operator, class, NORAD and COSPAR ids and TLE epoch with the element lines
on request, altitude, orbital speed and the sub-satellite point. CURRENTLY
OVER names the country and its first-level division, or the sea, and from
8x the most prominent populated place nearby, looked up offline from the
bundled Natural Earth packs (populated places are a new pack). The
satellites layer hides the ridden satellite's own marks, dims the other
orbit rings and ignores scene clicks and Escape while held. Exit restores
the camera's field of view and the satellite's follow camera.
C no longer enters or leaves Cockpit, bridge or orbit: it is left to the
app-wide CCTV shortcut, and Escape or the exit button leave first person.
Add indexed world triangles to the required globe contract on Cesium,
Navara and the fake engine. Own geometry, textures, visibility, depth tests,
pick identities and teardown inside each engine. Reused canvas images
request another frame until their new pixels upload. Move obstruction rays
and owned mesh, line and mover exclusions into the Cesium surface adapter.
Draw CCTV monitor planes and viewshed volumes through shared mesh handles.
Draw data-pack points, straight lines, raised polygons, image rectangles
and media captions through movers, lines and meshes. Delete the feature
renderers for each engine and retain decoded assets across scene rebinds.
Register the modules, dependency, format scope and reduced coupling baseline.
Cesium is the reference for geometry, colours, image placement, marker sizes,
caption offsets, picking and resource ownership. Navara now uses straight
pack lines and per-position polygon heights without edge densification.
Navara meshes draw in the transparent pass after the effects, with the
scene-depth test and the view's exposure undone, as the pack meshes did:
in the scene pass the landscape look's exposure washed a red pack image to
white. The scene-depth helper moves into the Navara engine, and the stems
helper that only its test still used is deleted. The mesh picker takes the
globe when Navara's depth read answers with the camera itself, which it
does right after another click and which hid a polygon from a quick second
click. The 256 px media caption is not pickable: its transparent square took
clicks meant for nearby pack features, and no action targets it. Annotation drapes remain on their existing
renderers because bounded building classification, animated fills and wire
cages need a separate contract extension to preserve Cesium behaviour.
Coupling falls from 344 to 210 Cesium layer-renderer lines, 1294 to 794 Navara
layer-renderer lines, 9 to 7 external Cesium import files, 1026 to 1005 viewer
lines, 9 to 6 extension reads and 42 to 39 engine-name lines. Other counts do
not grow. The full unit suite, import directions, package boundaries,
formatting and coupling checks pass. No user-visible strings change.
In the browser, keyless: the contract suite passes on Navara and on Cesium
apart from the camera-events check that also fails intermittently before
this change; CCTV v2 (58 checks), the CCTV and director engine switches pass,
with the pack image at the same pixel as before on both engines. The mesh
contract fixture now sits at 3 km, above the terrain Navara draws there. The
director interactions script starts at its view in the link so the app's
arrival flight no longer lands after the shot, and waits for the click's
asynchronous pick; its last step, the next-shot action, fails the same way
before this change.
Move Cesium viewer, map, sky, projection, trail and sonar internals into
its engine. Drive styles, bloom, sharpening and contact sonar through
engine.render. Count camera-input suspensions across all three engines
and migrate the feature callers. Preserve Cesium's effects and input
behaviour; fix transit lens clicks on Cesium and make zero sharpening on
Navara disable sharpening, as it does on Cesium.
Parse imported files into plain records and draw them through movers,
lines, ground and imagery on both engines. Follow Cesium's KML style
precedence, preserve local KMZ images and frame the drawable records.
Open overlays use imagery when the engine has no native map-overlay API.
Run every imported-display case on fake and headless Cesium engines.
The shared import path retains these known differences from Cesium's old
DataSources: wall fills, extruded prisms and gx:LatLonQuad are undrawn;
raised polygon fills drape while outlines retain altitude; GroundOverlay
rotation, altitude and RGB tint are not applied; rhumb lines become great
circles. Ground heights use the loaded surface synchronously instead of
waiting for most-detailed sampling. Supported KMZ ground overlays now
draw on Navara too. Document the limits rather than claiming full parity.
Combine batches B and C because their disjoint path manifests cannot
place the shared boundary and format files in a later commit while
keeping B independently valid. This combined commit owns the shared
docs, package boundaries, format scope, locale catalogs and coupling
baseline. Remove five obsolete import strings from all eleven catalogs.
Make the load-time source scan include new files and skip deleted paths
so the same checks work before and after recording the moves.
Coupling: Navara renderer lines fall from 1695 to 1294, Cesium import
files from 25 to 9, viewer lines from 1177 to 1026, SDK loads from 4 to 3
and engine-name lines from 62 to 42. No count grows.
The full unit suite and the boundary, format and coupling checks pass.
Browser validation remains to be run.
Browser checks to run
GOOGLE_MAPS_API_KEY= CESIUM_ION_TOKEN= npm run dev -- --port 4291
node scripts/qa-contract-suite.mjs http://localhost:4291
node scripts/qa-navara-visual-styles.mjs http://localhost:4291
node scripts/qa-navara-bloom.mjs http://localhost:4291
node scripts/qa-navara-sonar.mjs http://localhost:4291
QA_BASE_URL=http://localhost:4291 node scripts/qa-visual-effects.mjs
QA_BASE_URL=http://localhost:4291 node scripts/qa-zoom-out-guard.mjs
node scripts/qa-sky-model-lighting.mjs --url http://localhost:4291
node scripts/qa-geo-import.mjs http://localhost:4291
node scripts/qa-geo-import-regressions.mjs http://localhost:4291
node scripts/qa-geo-import-large.mjs http://localhost:4291
node scripts/qa-navara-geo-import.mjs http://localhost:4291
Use the keyless server for every check. Photoreal-only cases remain
unverified; do not enable paid tiles to run them.
Main's camera, voice, Cockpit and layer work now runs on the globe
contract where this branch draws through it:
- The voice tilt to an angle, its pivot about the place under the
crosshair and the frame limit from orbit run through the camera rig;
src/globeInView.js measures a camera pose with src/geo instead of a
Cesium camera. The free-look height limit and the globe-in-view
recovery stay Cesium-only for now.
- Cockpit takes main's own-layer entry rule. Double-clicking the
followed track goes through cockpitHost.routeDoubleClick, so it works
on both engines.
- Space Missions' failed-entry rollback and GhostMaps' age filter run
in the engine-neutral layer code. On Cesium an expired report is
dropped in place; on Navara its product is redrawn.
- Wildlife Sightings joins the catalog Cesium-only: Navara leaves it
out, and the shell builds its card only for the real layer, so a
Navara page no longer fails to start on its stand-in.
- One instruction sentence is shorter, which keeps the Realtime prompt
within its 55,500-character budget with the Sentinel-2 basemap.
Recent community wildlife observations, as a new Wildlife group in the
layer panel.
- Server: keyless iNaturalist proxy (v2 observations with only the
projected fields; undocumented v1 UTFGrid density tiles, decoded on the
server) sharing one paced queue (burst 4, ~1/s, 60/min, 8,000/day budget,
up to four in flight), in-memory caches and upstream-only cooldowns;
Acartia Salish Sea marine-mammal feed rounded to 0.01 degrees with
comments dropped.
- Layer: grid counts drawn as a soft view-ranked density glow; real sampled
sightings as taxon badges at their true positions with ambient photo
thumbnails and labels; obscured records stay privacy cells; a clicked
sighting stays pinned through camera moves and refreshes.
- Anchored photo card per selection with the single iNaturalist link;
attribution in the layer row and the Data attribution popover.
- Filters (CONFIRMED, taxa presets) round-trip in share links with token 9.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- src/geo drops the ported debug argument checks and the tests that
bitExact already covers; package boundaries accept a directory entry,
so src/geo/ is listed once per group.
- Cesium no longer carries movers and lines nobody draws with: they are
optional engine capabilities. The fake engine and the Cesium camera use
the shared cameraMath; unused contract tables, clock.ticked, the
'finest' surface detail and Navara click statistics are gone.
- ALPR keeps one overlay core and one presentation model for both
engines, bikeshare one station bookkeeping, the Navara ground effects
one definition helper; image atlases, view centres, mover banks and
colours are shared.
- The legacy switch route, the clock offset and rate controls and other
unused exports are removed.
- The acceptance runs the Navara draw, geo import and scope checks;
scenarios use the harness switch and page helpers; the main chunk
check keeps the size budget only and runs once in CI.
Main's new layers (drone airspace, OSM places, sea marks, ski pistes) are
Cesium-only for now: they join the layer directory and the Cesium catalog,
are built afresh for each Cesium catalog, and show as unavailable on Navara.
The zoom-out guard is a scene part that attaches to Cesium globes only. The
awareness range pulses and their echoes draw on Cesium through the awareness
display's viewer; Navara keeps the plain range ring. Camera-input wakes go
through the display, and the analyst keeps the session-owned memory with
main's area presentation. A disposed voice runner aborts work in flight, and
an engine switch still keeps the last answer.
Modules main added that read Cesium while they load (zoomOutGuard,
awarenessEcho, the ground-sampling scratch in localGeojsonCore) now build
those values on first use or use src/geo, so a Navara page still starts
without the SDK. The OSM view box reads the engine's view rectangle.
Main grew its own main chunk by 93,873 bytes gzip-9 since the merge base, so
the baseline is raised to the merged size.
Resolve the CURRENT-STATE.md layer table by keeping main's realigned
table and adding this layer's row. Main added an Arabic locale, so
translate the 12 layer strings and list ar.json in the drone-airspace
package.
Preserve the camera, map source and layer selection across engine sessions. Restore the previous engine if startup fails and retain local history navigation.
Core told time eight ways: Date.now() in most layers, JulianDate.now() for
flights and military, Cesium's clock frozen at page load for the sky, the
weather clock for observed imagery. They now read one clock,
src/time/worldClock.js, exported as ./time.
Live is a mode, not a timestamp: while live the clock is the wall clock,
so every layer draws as before. Away from live only the layers that follow
the clock move with it. The weather products follow it, as they followed
the weather transport; a host opts others in with follow(id).
- The weather clock takes its time from the world clock and drives its
replay, stepping its frames as before.
- Satellites, launches, flights and military (still 30 s and 15 s behind),
transit, GNSS, Recent Imagery, street level, the HUD and the celestial
ring read worldTimeFor(id). Cadences stay on the wall clock.
- Cesium's sky (stars, sun, moon, atmosphere, lighting) draws at the world
time each frame instead of the moment the page loaded.
- Weather frames and satellites declare coverage() for a host's timeline.
The application views install one vue-i18n instance that holds the active
locale and the catalogs. src/i18n/index.js stays free of Vue: bindLocale()
keeps its locale in the instance's ref, so t(), tn() and the Intl formatters
read it from there and a view that translates while rendering renders again
when the locale changes. A tag written straight to the ref is normalized.
Message formatting, catalogs and call sites are unchanged, and switching
language still reloads the page.
The instance outlives the views: unmounting one no longer disposes it. The
browser build defines vue-i18n's feature flags and drops its message
compiler, which Core does not use.
index.html now lists each view's host and the loading screen itself, so
the gev:template expansion plugin, its view list, the loading-screen
template and the ./build/html export are gone. createApplication takes
omitViews for views an application leaves out; their hosts are removed,
and a view an earlier call rendered is unmounted.
The scene chrome, cockpit, Display controls, command dock, layer panels,
Context rail, welcome card and Provider Settings move verbatim into
src/ui/views and mount into their hosts before the page is translated and
wired. Each view renders the same elements, attributes and text as its
template; Vue keeps whitespace between elements as one space, and no view
markup sits under a white-space: pre rule.
Template asset URLs stay literal, view files are left out of Prettier so
the markup is not reflowed, and the Node component loader moves to build/
and is exported as build/register-vue for host test runners. Hosts that do
not initialize Core's locale pass localePicker: false instead of editing
the markup.
Preserve native clustering, selection and direct-action playback through renderer ports. Record both-engine dev/preview, legacy, contract, shell and build evidence with remaining W2 limits.
Move the share-link token to 6 (3 is gnss-integrity), build altitude
ranges from LOWER/UPPER VAL, UOM and CODE, mark card and legend strings
for translation, rename the layer to Prohibited & Restricted Airspace,
move the changelog entry to changelog.d, and keep the layer voice-off
while the Realtime prompt is at its budget.
* feat(share): export the share-view codec for host servers
Add `@gods-eye-view/core/share/view-codec` (src/share/viewCodec.js) as a
package export with its own boundary group. The codec is pure (no DOM, no
Cesium, no external dependencies), so a host's server can decode a
/share/<code> link, for example to describe the view in link previews.
* feat(share): export the share-link address helpers
Add `@gods-eye-view/core/share/link-path` (src/share/shareLinkPath.js), so a
host's server reads /share/<code> addresses the way the app does.
* feat(street-level): export the KartaView and Panoramax providers
A host that composes Street Level with its own provider list can already
import the Mapillary and Google Street View providers; KartaView and Panoramax
were reachable only through the default list, which needs a Mapillary source.
Exporting them lets a host offer the keyless providers without Mapillary.
* chore: changelog entry for the Street Level provider exports
* feat(street-level): send Mapillary Graph lookups to a source endpoint
A Mapillary source can take a graphEndpoint, a Graph API base that adds
the token itself. Its sequence lookups go there through the source's
transport, and the photo viewer uses MapillaryJS's stock GraphDataProvider
at that endpoint, so a host that proxies Mapillary needs no client token in
the page. Without it, behaviour is unchanged: direct Graph API calls with
the client token.
Main gained three imagery providers (Google Street View, KartaView,
Panoramax), shared line-coverage, cone and photo-viewer modules, one source
lit at a time and the translated interface. Their shared modules still
carried what this branch removes, so the merge ports them onto it:
- lineCoverage.js is this branch's coverage.js made provider-neutral
(draped lines near the ground only, the selection drawn over them, key
and rate-limit blocking); sequenceCones.js is sequences.js the same way.
Mapillary, KartaView and Panoramax all build on them; Mapillary moves
back to providers/mapillary/ beside the others.
- No ground casting, mesh sampling, surface modes, FOLLOW, legend, prewarm,
orbit overview points (Panoramax's density dots included), stale-tile
swaps or water-filled point budget come back.
- Kept from main because the new providers need them: requiresMapStack and
attachMapStackController (Street View on Google 3D only), ground clicks,
openNearest with an optional provider nearestImage, openImage {frame},
reportError, the view-cone marker (now an SVG glyph), filters /
coverageLabel / sinceBasis, OPEN STREET VIEW, keyAlternatives and i18n.
- photoViewer.js treats the viewer as expanded when its wrap is full screen
or the iPhone popover, as EXPAND now is.
- The fixture browser gate keeps its 8 steps and adds one each for
KartaView and Panoramax; catalogs, docs, boundaries and format scope
follow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reconcile #880 with current main. Delegate the view-state builder while keeping enabled-only layers, disabledLayerCount, provenance, context semantics, and existing voice behavior. Add parity and roster-read regressions; retain package export, boundaries and changelog fragment.
Preserve original GEV voice, layer and packet contributor credit.
scripts/check-main-chunk.mjs (npm run check:main-chunk) finds the main
chunk of a build (the Vite manifest's entry, or the module script
dist/index.html loads), follows its static imports and fails when any of
them carries three or Navara markers; it prints the sizes. Base build:
main chunk 2,850,229 bytes (gzip 881,783), no static imports, clean.
scripts/qa-w1-baseline.mjs records Cesium mode for the W1 layers (flights,
military, local ADS-B, vessels, satellites, launches): counts, five known
objects with positions and screen points, JPEGs (path, size, sha256) at
the global / New York (JFK) / orbit poses, a tracked object's camera pose
after 2 s and what its share link restores. Live sources until the
fixture sources of Task 8.
Upstream's #568 adds the Air quality layer to the Weather group (ECCC
AQHI, Canada only) with a band legend, worst-first list and voice
support.
- package-boundaries: both sides' entries unioned.
- layerPanel/weatherPanel tests: both sides' new tests kept; the
weatherPanel fixture follows upstream (legendCategorical: true, the
'list' block id) while our localized-history (K-6) test stays.
- air-quality strings marked: band labels and the provider label as
msg() data, the readings-list aria-label through t(); es/ru entries
added (+6 keys each).
- The synthetic air-quality allocation-benchmark titles follow the
file's own convention (// i18n-ignore) like TRK/VESSEL/CAMERA.
Also verified the auto-merged weatherPanel.js against upstream: ORDER
gains 'air-quality', OBSERVED stays slice(2, 5), and the data-driven
legendCategorical path is upstream's.
npm run i18n: complete (2559), no unmarked strings. npm test: only the
pre-existing environmental OPENAI_API_KEY leak fails. Build passes.
Port to current main: main reserved layer-state token 0 for street-level, so
air-quality takes the next free token, 4 (registry, ledger, and the allocation
tests shift by one). Main moved source contracts into sourceComposition.js, so
air-quality registers its getSnapshot contract there. Package boundaries keep
main's lists plus this branch's air-quality modules and packages.
Pins @navaramap/three and @navaramap/three-default-plugin 0.2.1 with
their peers three 0.185.1 and postprocessing 6.39.4 (mapillary-js keeps
its own nested three 0.134). The dev server leaves the Navara packages
and their wasm workers out of dependency pre-bundling so wasm-bindgen
resolves its .wasm next to the package. Nothing imports Navara yet: the
Cesium-mode main chunk is byte-identical (2,837,992 bytes, same hash).
Conflicts resolved by keeping both sides: the Events group gains
gnss-integrity with a translated label, the cockpit aircraft line shows
main's transponder emergency through a localized formatter (cockpitMath.js
stays i18n-free, like the Context scope line), and the boundary lists keep
both the i18n modules and main's new data modules.
Main's new UI is translated into es and ru: the GNSS Integrity row, legend
and notes; geo file import (button, hints, list actions, every import
error, with numbers passed as placeholders); transponder status names.
Also formats kartaview/viewer.js so format:check passes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Upstream's Street View rework (second imagery provider, one source at a
time, OPEN STREET VIEW button, key-alternative guidance) rewrote exactly
the street-level regions this branch localizes.
Resolved as upstream logic + re-applied localization:
- keySetupCore: new unlock copy kept as msg() data; added
keySetupAlternativeKeyNames() returning machine values (registry key
names) so key-setup core stays English while presentation localizes.
- New src/ui/keyGuidance.js: localized key-guidance composer shared by
layerPanel and streetLevelPresentation (requirement sentence plus the
"or light X to use your Y key" alternatives tail, placeholders only).
- streetLevelPresentation: keyGuidance()/presentNearest()/presentMeta
relocalized (t/tn + formatNumber instead of toLocaleString);
renderModes and month-precision dates kept from upstream.
- layer-panels.html: upstream row ids, sl-meta-row and the new button
kept, data-i18n hooks re-applied (incl. OPEN STREET VIEW fallback).
- es/ru catalogs: 13 new keys, stale unlock key replaced in place.
npm run i18n: complete for es and ru. npm test: 6345/6347; the one
failure (devFreshDotenv provenance) also fails on plain upstream/main
and on the pre-merge tip here - this machine leaks OPENAI_API_KEY into
spawned children with a replaced env, so it is environmental.
Bins airborne ADS-B v1/v2 aircraft that report degraded navigation
integrity (NIC < 7, NACp < 8, or readsb's GPS-loss flag) into 0.5 degree
cells over a rolling 30-minute window, coloured with the gpsjam.org
bands. Keyless: reads the capped adsb.lol point snapshot around the view
through /api/gnss-integrity, which returns integrity fields only, caches
60 s per whole-degree anchor, backs off on upstream 429 and serves the
last snapshot on failure. Adds scripts/qa-gnss.mjs.
Built by the Hex project (Crew Leo Agile dev team).
Co-Authored-By: Claude <[email protected]>
main grew the Street Level provider registry while this branch removed it
(#982–#985: injected providers, an empty composition, the `r` switch field).
The registry, params, uiState and pick router come back as main has them and
the slimmed layer sits behind the provider contract:
- The core (index.js) drives providers as on main, without surface modes,
ground casting, FOLLOW, prewarm and nearest-image lookups. A provider's key
status is asked with a 10 s timeout and asked again on a switch-on while no
key is known.
- Mapillary is mapillaryProvider.js over the slim coverage, sequences and
viewer modules, which read the filter and the active flag from the provider
context.
- The viewer host mounts one provider adapter at a time, no prewarm.
- The panel has a chip per provider again (the pill switches the layer);
no legend, EXPAND stays the Fullscreen API.
- The contract drops what no provider implements: groundCaster, meshSampler,
getSurface/setSurface, nearestImage, prewarm, reportError, the legend and
surface fields of the UI snapshot.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Show US prohibited and restricted special-use airspace from the keyless
FAA SUA FeatureServer as an off-by-default Infrastructure layer, with
share-link token 3 and an informational-only disclaimer.
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]>
- No pointer cursor over coverage lines (no other layer has one); a click
test replaces the hover tests.
- No legend: one source, one colour.
- The on-globe credit lives in index.js; credits.js is gone.
- Billboard glyphs are SVG data URLs with the same geometry, so tests need
no canvas stub.
- The tile cache keeps the 120 most recently used tiles, same 24 h TTL,
instead of a byte budget; listTileLayers was test-only.
- providers/mapillary/ is flattened into the layer and its policy merged
into one policy.js; the source's method list is a literal in the catalog.
- One fakeCesiumViewer for the layer tests.
- The fixture gate keeps the steps the unit tests do not cover: coverage and
credit, a rejected key, line -> cone -> photo, close, a keyless page.
Co-Authored-By: Claude Fable 5.1 <[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]>
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]>
PR #768 bundled a portable-panel windowing system with the Street Level
layer. Remove it so CCTV and Street Level are ordinary docked right-rail
panels again, as on main before the merge.
Removed:
- header drag to float, 8-way edge/corner resize, double-press snap-back,
dockOnCollapse, command-dock clearance, float hint toast, floating z floor,
persisted floating geometry and ResizeObserver re-clamping
(panelPositionControls.js restored to its pre-#768 version)
- src/ui/panelResize.js and its tests, panelDock.test.mjs
- onPanelResized/dockPanel wiring in PanelChrome, the application shell and
StreetLevelControls (EXPAND/SHRINK no longer docks)
- :not(.panel-floating) guards and .panel-floating/.panel-portable/resize CSS
- qa:panel-resize, qa-panel-resize.mjs, qa-panelDrag.mjs, their tests, the
CI "Portable panel gate" step, QA_FAIL_ON_RETRY, and the Street Level
gate's float/resize/dock steps
- floating-panel docs in README, CHANGELOG, CURRENT-STATE, KNOWN-ISSUES
Kept: the Street Level panel as a docked rail panel (share/cockpit/rail
lists, layout controller, rail and Cyber CSS, EXPAND viewer), and the
independent right-rail alignment fix (settledLeftRailTop, 9dde7b4).
Co-Authored-By: Claude Opus 5.5 <[email protected]>