mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
Merge branch 'main' into docs/add-codespaces-deployment
This commit is contained in:
378 files changed
+25505
-1584
No files matched your search
+5
-2
@@ -115,7 +115,8 @@ GEMINI_LIVE_MODEL=gemini-3.8-live
|
|||||||
# Client Token). Client-exposed by design, like GOOGLE_MAPS_API_KEY.
|
# Client Token). Client-exposed by design, like GOOGLE_MAPS_API_KEY.
|
||||||
MAPILLARY_CLIENT_TOKEN=
|
MAPILLARY_CLIENT_TOKEN=
|
||||||
|
|
||||||
# Optional: OpenSky auth mode (oauth | basic | auto | anon)
|
# Optional: OpenSky auth mode (oauth | auto | anon). OpenSky retired
|
||||||
|
# username/password (basic) sign-in on 2026-03-18; "basic" now loads anonymously.
|
||||||
# Default: oauth — requires client credentials below
|
# Default: oauth — requires client credentials below
|
||||||
# Set to "anon" to skip aircraft auth (rate-limited but works)
|
# Set to "anon" to skip aircraft auth (rate-limited but works)
|
||||||
OPENSKY_AUTH_MODE=oauth
|
OPENSKY_AUTH_MODE=oauth
|
||||||
@@ -128,7 +129,9 @@ OPENSKY_CLIENT_SECRET=
|
|||||||
# Optional paid Patreon tokens raise request limits; there is no free API key.
|
# Optional paid Patreon tokens raise request limits; there is no free API key.
|
||||||
LL2_API_TOKEN=
|
LL2_API_TOKEN=
|
||||||
|
|
||||||
# Optional: OpenSky credentials JSON file path (alternative to above)
|
# Optional: OpenSky credentials JSON file path, used when the two values above
|
||||||
|
# are empty: the credentials.json your OpenSky account page downloads when you
|
||||||
|
# create an API client. Use an absolute path for compatibility with launchers.
|
||||||
# OPENSKY_CREDENTIALS_FILE=/path/to/credentials.json
|
# OPENSKY_CREDENTIALS_FILE=/path/to/credentials.json
|
||||||
|
|
||||||
# Optional: dev server config
|
# Optional: dev server config
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
## What this changes
|
||||||
|
|
||||||
|
<!-- One or two sentences: what changed and why. Link the issue if there is one. -->
|
||||||
|
|
||||||
|
## How I verified it
|
||||||
|
|
||||||
|
- [ ] `npm run build`
|
||||||
|
- [ ] `npm test`
|
||||||
|
- [ ] `npm run test:track` (dev server running)
|
||||||
|
- [ ] Feature gate for the area I touched: <!-- e.g. `node scripts/qa-radio.mjs`, or "none applies" -->
|
||||||
|
|
||||||
|
<!-- Screenshots are welcome for anything visual. -->
|
||||||
|
|
||||||
|
## New layer checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Fill this in if the PR adds a new data layer. Delete the whole section if it doesn't.
|
||||||
|
See "Adding a new layer" in CONTRIBUTING.md.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
|
||||||
|
<!-- At least one screenshot of the layer on the globe, plus its info card or panel if it has one. -->
|
||||||
|
|
||||||
|
**Recording**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Preferably 15 to 30 seconds: turn the layer on, move around, open a record.
|
||||||
|
Drag an .mp4 or .mov in here; GitHub accepts up to 10 MB on free accounts.
|
||||||
|
If it's bigger, link to it instead.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Proposed category**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Which layer-panel group should it sit in? The current groups are in PANEL_GROUPS
|
||||||
|
in src/ui/layerPanel.js: Movement, Cameras, Infrastructure, Events, Weather, Utilities.
|
||||||
|
If none fits, propose a new group and say why.
|
||||||
|
-->
|
||||||
|
|
||||||
|
Category:
|
||||||
|
|
||||||
|
**Data source and licence**
|
||||||
|
|
||||||
|
- Source (provider and endpoint):
|
||||||
|
- Licence or terms (link):
|
||||||
|
- Attribution shown in the app:
|
||||||
|
- Needs a key? (none / optional / required):
|
||||||
|
- Fetched at runtime, or bundled in the repo:
|
||||||
|
|
||||||
|
<!-- Bundled data must be data we have the right to redistribute. -->
|
||||||
|
|
||||||
|
- [ ] Added a row to `DATA_SOURCES.md`
|
||||||
|
- [ ] Credit is registered in `src/data/dataCredits.js`
|
||||||
|
- [ ] Share-link token assigned with `npm run layer-token:next`, if the layer is shareable
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [ ] `docs/CURRENT-STATE.md` updated and a `changelog.d/<pr-number>-<slug>.md` fragment added, or this PR doesn't change runtime behaviour
|
||||||
+720
-29
@@ -1,11 +1,707 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.3.0] — 2026-10-27 — Street Level, On Your Phone, in Your Language
|
||||||
|
|
||||||
|
- Show a Street Level view facing 359.5° or more as 0°. The viewer caption
|
||||||
|
rounded the bearing without wrapping, so a view facing 359.6° read "360°"
|
||||||
|
(Aditya, @Rohilalala, #1431).
|
||||||
|
|
||||||
|
- The visual styles and their presets mark their labels with the catalog-free
|
||||||
|
`msg()`, so the share-link codec, and any server that loads it, no longer
|
||||||
|
bundles every translation catalog (about 0.9 MB) (samehkhamis, #1421).
|
||||||
|
|
||||||
|
- Keep a location search that found nothing, or failed, in the field so it can
|
||||||
|
be corrected: the query stays, focused and selected, and a persistent
|
||||||
|
announced message under the search row says what to do next, instead of a
|
||||||
|
toast followed by a cleared and closed field (samehkhamis, #1420).
|
||||||
|
|
||||||
|
- Hosts can read share links on their server: the share-link codec and the
|
||||||
|
`/share/<code>` address helpers are now package exports,
|
||||||
|
`@gods-eye-view/core/share/view-codec` and `@gods-eye-view/core/share/link-path`
|
||||||
|
(samehkhamis, #1419).
|
||||||
|
|
||||||
|
- Share links are now short: the share button makes `/share/<code>` links
|
||||||
|
that open on any copy of the app, follow the aircraft, military aircraft or
|
||||||
|
satellite you were following, and fall back to its last-known position with
|
||||||
|
a notice if it isn't reporting. A mistyped link is refused instead of
|
||||||
|
opening a different view. Long `#v=2` links keep working (samehkhamis, #1418).
|
||||||
|
|
||||||
|
- Make Clear All clear the saved and shared layer selection. Layers that a
|
||||||
|
context mode had already switched off no longer stay in the share link and
|
||||||
|
come back on reload (samehkhamis, #1417).
|
||||||
|
|
||||||
|
- Restore a share link opened in an already-open tab. Pasting a link into the
|
||||||
|
address bar, following one, or using Back/Forward now moves the camera and
|
||||||
|
restores the style, map, panels and layers instead of changing only the URL
|
||||||
|
(samehkhamis, #1416).
|
||||||
|
|
||||||
|
- Add a Scope checkbox and extend feathering to 200%, retaining scope settings
|
||||||
|
across HUD layouts, share links and scenes. First run opens with Scope ON at
|
||||||
|
100% feather; explicit settings and historical links retain their values. Give
|
||||||
|
Tactical, Operator and Minimal text black backplates only when Scope is OFF,
|
||||||
|
including vertical metadata, without changing the original HUD text color, glow or
|
||||||
|
opacity. Scope ON retains its original HUD presentation (bilawalsidhu, #1410).
|
||||||
|
Keep title and tagline positions stable when toggling Scope, include the eye
|
||||||
|
in the title backing, and brighten the two secondary labels only with Scope OFF.
|
||||||
|
|
||||||
|
- Remove four modules nothing in the app imports: `src/app/sourceSlot.js`,
|
||||||
|
`src/data/flowTiles.js`, `src/data/issPass.js` and `src/data/localGeojson.js`.
|
||||||
|
The local GeoJSON layer tests now build layers the way the catalog does, so
|
||||||
|
they keep covering `localGeojsonCore.js`. Package exports are unchanged
|
||||||
|
(samehkhamis, #1403).
|
||||||
|
|
||||||
|
- Aircraft on the ground keep getting ground-height samples after the
|
||||||
|
computer's clock steps back, instead of waiting for it to catch up
|
||||||
|
(quanticsoul4772, #1399).
|
||||||
|
|
||||||
|
- The Sharpen slider at 0 now turns the sharpen pass off instead of still
|
||||||
|
sharpening at a low amount (quanticsoul4772, #1398).
|
||||||
|
|
||||||
|
- Show a locally heard aircraft tracking 359.5° or more as TRK 0°. The Local
|
||||||
|
ADS-B card rounded the track without wrapping, so 359.7° read "TRK 360°"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1396).
|
||||||
|
|
||||||
|
- Report a fire detection's acquisition time as a UTC timestamp in
|
||||||
|
`get_active_fires`. The tool joined FIRMS's unpadded date and time, so a
|
||||||
|
00:45 UTC detection read "2026-01-01 45"; it now reads
|
||||||
|
`2026-01-01T00:45:00.000Z`. Missing or invalid dates and times remain
|
||||||
|
unknown rather than rolling into another calendar day
|
||||||
|
(Rudy Celekli, @rudycelekli, #1395).
|
||||||
|
|
||||||
|
- Report Docker container health with a local HTTP check using Node, with the
|
||||||
|
same port parsing as the launcher (John Rogers, @14traits, #1394).
|
||||||
|
|
||||||
|
- Start the Docker install on SELinux hosts such as Fedora and RHEL. Compose now
|
||||||
|
mounts the checkout with `:z`; without it the container could not read `/app`
|
||||||
|
and stopped with "Cannot find module '/app/scripts/docker-start.mjs'"
|
||||||
|
(Anıl Güvenç, @anilguvenc, #1391).
|
||||||
|
|
||||||
|
- Render awareness-overlay bearings and courses that round through north as
|
||||||
|
`000°`, rather than `360°` (Rudy Celekli, #1390).
|
||||||
|
|
||||||
|
- Show cockpit wind from 359.5° or more as 0°, not 360°. Rounding 359.6°
|
||||||
|
printed "N · 360°" on the local wind direction
|
||||||
|
(Rudy Celekli, @rudycelekli, #1389).
|
||||||
|
|
||||||
|
- Show a drawn line of 999.5 m or more as 1.0 km. The draw hint kept metres
|
||||||
|
below 1000, then rounded 999.6 m up to "1000 m"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1388).
|
||||||
|
|
||||||
|
- Show a Contacts entry 999.5 m or more away as 1.0 km. The panel kept meters
|
||||||
|
below 1000, then rounded 999.6 m up to "1000 m"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1387).
|
||||||
|
|
||||||
|
- Show a route leg of 995 m or more as 1.0 km. The route summary and step
|
||||||
|
cards kept meters below 1000, then rounded 996 m up to "1000 m"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1386).
|
||||||
|
|
||||||
|
- Show a route leg of 59.5 seconds or more as 1 min. The route summary and step
|
||||||
|
cards kept seconds below 60, then rounded 59.6 s up to "60 s"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1385).
|
||||||
|
|
||||||
|
- The HUD's AI place summary drops a reply that arrives after the camera has
|
||||||
|
started moving, so a summary asked for before a flight no longer names the
|
||||||
|
old place mid-flight. Cancelled or partially typed summaries retry even if the place
|
||||||
|
context stays the same (quanticsoul4772, #1384).
|
||||||
|
|
||||||
|
- Reduce repeated work in aircraft detection candidates by caching unchanged
|
||||||
|
callsign/registration text and reusing resolved fleet models. Metadata updates,
|
||||||
|
live positions and tracked-aircraft/model handoffs remain immediate
|
||||||
|
(samehkhamis, #1379).
|
||||||
|
|
||||||
|
- Increase touch-first globe zoom sensitivity to three times the default while
|
||||||
|
retaining Cesium collision and distance limits.
|
||||||
|
- Reuse one delayed display timestamp per civil-aircraft fleet update and cache
|
||||||
|
extrapolation coordinate frames until their fix coordinates change. Preserve
|
||||||
|
contact counts, movement cadence, tracked motion and ground corrections.
|
||||||
|
|
||||||
|
- Turning a layer off frees the memory its entities held. Cesium kept
|
||||||
|
removed layers' draw commands in reused per-pass arrays, so Data Centers
|
||||||
|
kept 116 MiB after it was switched off, submarine cables 74 MiB and dams
|
||||||
|
30 MiB; they now keep 13, 9 and 9 MiB (quanticsoul4772, #1367).
|
||||||
|
|
||||||
|
- Aircraft 3D models (Flights, Military Flights, Local ADS-B) skip Cesium's
|
||||||
|
dynamic environment map, a sky render and GPU readback per model. With
|
||||||
|
~150 models in the scene at Atlanta on Google 3D, GPU time per frame at
|
||||||
|
rest falls from 14.3-14.8 ms to 12.1-12.5 ms; a same-model comparison
|
||||||
|
showed no visible change (quanticsoul4772, #1366).
|
||||||
|
|
||||||
|
- Drop the decimal when fire radiative power rounds to 10 MW. A 9.96 MW
|
||||||
|
detection was labeled 10.0 MW because the one-decimal cutoff ran before
|
||||||
|
rounding (Rudy Celekli, @rudycelekli, #1354).
|
||||||
|
|
||||||
|
- Count a transit report that is 90 seconds old as one minute. The card kept
|
||||||
|
seconds through 89s, then rounded 90s up to "Reported 2 min ago"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1353).
|
||||||
|
|
||||||
|
- Read a destination heading that rounds to north as 000. 359.6° was shown as
|
||||||
|
360° on the destination bearing while the compass tape already uses 000
|
||||||
|
(Rudy Celekli, @rudycelekli, #1352).
|
||||||
|
|
||||||
|
- Keep a tracked vessel's speed, course, heading and fix time when AISStream
|
||||||
|
static data arrives; the static message only merges name, type, destination
|
||||||
|
and IMO (rudycelekli, #1351).
|
||||||
|
- Apply changed static metadata immediately, retaining known fields when a
|
||||||
|
later static report omits them, without refreshing the vessel's position
|
||||||
|
(samehkhamis, #1351).
|
||||||
|
|
||||||
|
- Convert NHC cyclone movement speed from miles per hour to knots, so
|
||||||
|
`movement.speedKt` in cyclone query results is no longer about 15% high
|
||||||
|
(rudycelekli, #1350).
|
||||||
|
|
||||||
|
- Label NHC cyclone classifications by their published meanings: `PTC` is a
|
||||||
|
post-tropical cyclone, `PC` a potential tropical cyclone, and subtropical
|
||||||
|
systems arrive as `STS` and `STD` (rudycelekli, #1349).
|
||||||
|
|
||||||
|
- Wake staged weather imagery on a 150 ms timer while waiting for readiness,
|
||||||
|
avoiding a continuous render loop and cancelling the wake when work ends
|
||||||
|
(@nu-stats, #1345).
|
||||||
|
|
||||||
|
- Phrase slight and sharp ramps as directions, so a ramp reads "slightly left"
|
||||||
|
instead of "on the slightly left", and a lane names its side in both
|
||||||
|
the proxy response and localized browser instructions
|
||||||
|
(Rudy Celekli, @rudycelekli, #1343).
|
||||||
|
|
||||||
|
- Keep an impossible wildfire acquisition date off the detection clock, so a
|
||||||
|
fire is not labeled with a later day it did not happen
|
||||||
|
(Rudy Celekli, @rudycelekli, #1342).
|
||||||
|
|
||||||
|
- Reject impossible KartaView photo timestamps, so 31 February is unknown
|
||||||
|
instead of rolling forward into March and passing the imagery date filter
|
||||||
|
(Rudy Celekli, @rudycelekli, #1341).
|
||||||
|
|
||||||
|
- Read a hemisphere letter that is separated from its number, so a search for
|
||||||
|
`40.7128 N 74.0060 W` flies there instead of falling through to the geocoders
|
||||||
|
(Rudy Celekli, @rudycelekli, #1340).
|
||||||
|
|
||||||
|
- Carry HUD LAT/LON seconds that round up to 60.00" into the minutes and
|
||||||
|
degrees, so longitude -122.35 reads `122°21'00.00"W` instead of
|
||||||
|
`122°20'60.00"W` (rudycelekli, #1339).
|
||||||
|
|
||||||
|
- Improve touch selection of aircraft and vessels through the shared contact
|
||||||
|
picker, reveal tracked aircraft details when opening Context, support downward
|
||||||
|
swipe dismissal of phone drawers while preserving scrolling and controls, and
|
||||||
|
enlarge the phone logo (samehkhamis, #1338).
|
||||||
|
|
||||||
|
- Evict terrain-height cache entries in fetch-time order without repeatedly
|
||||||
|
sorting the full cache, while keeping refreshed points newest and ordering
|
||||||
|
restored entries by their fetch time (daikaginza, #1326).
|
||||||
|
|
||||||
|
- Add a phone layout for viewports 720px and narrower: the globe stays full
|
||||||
|
screen and a bottom tab bar (Layers, Cameras, Context, Search, More) opens one
|
||||||
|
panel sheet at a time, keeping attribution visible and scene STOP reachable.
|
||||||
|
Adapted from the phone tab-shell in #203 by blearymoth (samehkhamis, #1325).
|
||||||
|
|
||||||
|
- Export the KartaView and Panoramax Street Level providers, so a host composing
|
||||||
|
its own provider list can offer the keyless providers without Mapillary
|
||||||
|
(samehkhamis, #1324).
|
||||||
|
- A Mapillary source can take a `graphEndpoint`, a Graph API base that adds the
|
||||||
|
token itself (such as a same-origin proxy). Its sequence lookups go there
|
||||||
|
through the source's transport, and the photo viewer uses MapillaryJS's
|
||||||
|
Graph provider at that endpoint, so no client token is needed in the page
|
||||||
|
(samehkhamis, #1324).
|
||||||
|
|
||||||
|
- Remove dead code across the repository: three modules nothing imports
|
||||||
|
(`src/ui.js`, `src/standalone/ui.js`, `src/data/localLayers.js`), exports
|
||||||
|
and locals nothing reads, a duplicate copy of the bundled dams data and an
|
||||||
|
unused ship model. The app no longer builds an unused default wind layer
|
||||||
|
and Bhote Koshi locator layer when it loads. Package exports are unchanged
|
||||||
|
(samehkhamis, #1321).
|
||||||
|
|
||||||
|
- Validate provider response formats, strengthen camera source checks, and apply consistent API response protections (samehkhamis, #1317).
|
||||||
|
|
||||||
|
- Clarify shared field CSS ownership: CCTV owns calibration editor sizing,
|
||||||
|
while Radio and Cyber supply explicit field palettes without changing the
|
||||||
|
current appearance or native control behavior (mazy1998, #1309).
|
||||||
|
|
||||||
|
- Share read-only status badge presentation across CCTV source/frame labels,
|
||||||
|
calibration provenance and Weather coverage, preserving status meanings,
|
||||||
|
palettes, translations and announcements while preparing the view contract
|
||||||
|
for a future Vue migration (mazy1998, #1306).
|
||||||
|
|
||||||
|
- Share native dropdown and numeric-field styling across Scene, CCTV, Display
|
||||||
|
and Radio controls, with a shared CCTV number-input builder that preserves
|
||||||
|
existing precision, validation and commit behavior (mazy1998, #1305).
|
||||||
|
|
||||||
|
- Point distances wrap longitude differences across the antimeridian, in voice
|
||||||
|
proximity checks and `places_nearby` (quanticsoul4772, #1304).
|
||||||
|
|
||||||
|
- `get_wind` flags stale forecasts (`stale`) when the wind proxy serves a
|
||||||
|
cached model run (quanticsoul4772, #1303).
|
||||||
|
|
||||||
|
- Give ordinary Scene and CCTV actions shared native buttons with consistent
|
||||||
|
default, destructive and compact styling while preserving their existing
|
||||||
|
behavior and specialized controls (mazy1998, #1302).
|
||||||
|
|
||||||
|
- Unify segmented selectors for model coverage, detection allocation, drawing
|
||||||
|
shape, Street View and imagery comparison while preserving their existing
|
||||||
|
behavior. Keep compact labels such as FILL and FLAT on one line, and let
|
||||||
|
collapsed Weather release space to the open rail panel. Reserve the full mode
|
||||||
|
button height in Recent Imagery so keyboard focus is not clipped (mazy1998, #1301).
|
||||||
|
|
||||||
|
- Add a shared Voice Preview and Settings panel with browser-local preferences,
|
||||||
|
inactivity controls and accessible floating provider controls on compact layouts;
|
||||||
|
recover inactivity after confirmed audio-less replies, release retired audio
|
||||||
|
observers and keep cost warnings visible outside Settings
|
||||||
|
(manjunath22466, #1299).
|
||||||
|
|
||||||
|
- Add Simplified Chinese (zh-CN) to the language picker, with localized dates
|
||||||
|
and readable Chinese labels. Based on translations by @abiyeenzo (#914)
|
||||||
|
and @EDIT-Aeterna (#979).
|
||||||
|
(German, @gekh, #1298).
|
||||||
|
|
||||||
|
- Add the S2 GhostMaps layer: S2 Underground's public KMZ intel products
|
||||||
|
(Common Intelligence Picture, 30-day kinetic pull, Border Crisis Map, Iran
|
||||||
|
and Yemen products and the newest incident drop) from
|
||||||
|
github.com/s2underground/GhostMaps, with one chip per product and report
|
||||||
|
cards on click. `/api/ghostmaps` watches the repository's branch head every
|
||||||
|
15 s and pushes each new commit to open pages, which reload only the
|
||||||
|
products that changed (Black Pine Defense).
|
||||||
|
- Clear a temporary GhostMaps upstream error and notify open pages when the
|
||||||
|
repository becomes reachable again, including when no new edition was published.
|
||||||
|
- Release packed-image resources when a load is cancelled or fails, respect
|
||||||
|
injected or omitted GhostMaps sources, and wait for background cache warm-up
|
||||||
|
before removing provider test fixtures.
|
||||||
|
- Include Simplified Chinese translations and keep the combined voice prompt
|
||||||
|
within its existing size limit while retaining all spoken aliases.
|
||||||
|
|
||||||
|
- A Street Level provider whose `zoomFor` still answers `{zoom, kind}`, the
|
||||||
|
shape before the core went slim, draws its lines again instead of a silent
|
||||||
|
blank; an overview level or any answer that is not a whole zoom shows the
|
||||||
|
zoom-in hint (asturksever, #1288).
|
||||||
|
|
||||||
|
- Give ordinary sliders a shared native control and consistent track, thumb,
|
||||||
|
value, focus and disabled styling while preserving feature behavior and
|
||||||
|
specialized controls (mazy1998, #1287).
|
||||||
|
|
||||||
|
- The Street Level SINCE note sits on the value's line, in its type and a
|
||||||
|
size smaller: with KartaView lit it reads ANY DATE (by upload date). It
|
||||||
|
had lost its size variable in the Street Level slim-down and rendered
|
||||||
|
larger than the value, on a line of its own (asturksever, #1286).
|
||||||
|
|
||||||
|
- Keep aircraft route labels on the scheduled airport leg instead of accepting
|
||||||
|
its distant great-circle extensions, preserving generous endpoint tolerance
|
||||||
|
across the signed-angle branch cut (rudycelekli, #1284).
|
||||||
|
|
||||||
|
- Report cached CelesTrak satellite records as stale in the Data Layers row,
|
||||||
|
preserving separate core and dense catalog provenance through refresh,
|
||||||
|
removal and lifecycle cleanup (rudycelekli, #1283).
|
||||||
|
|
||||||
|
- Pause periodic network updates in hidden tabs; the overdue update on return restarts the polling schedule, so the next poll follows one interval later even when that update fails (asturksever, #1282).
|
||||||
|
- Report and guard browser visibility during Transit QA so hidden-page polling pauses cannot masquerade as playback regressions (asturksever, #1282).
|
||||||
|
- Require each live city to supply its own feed vehicles in Transit QA; retained vehicles cannot qualify another city (asturksever, #1282).
|
||||||
|
- Bound the Transit card-anchor probe by observed poll coverage instead of a fixed 20-second window (asturksever, #1282).
|
||||||
|
- Keep visible-page Transit probes focused through the browser testing API (asturksever, #1282).
|
||||||
|
- Add a real-browser regression gate for hidden-tab periodic work, explicit refresh, resume and teardown (asturksever, #1282).
|
||||||
|
- Restore minimized test windows before screenshots so native compositor pauses cannot stall browser acceptance (asturksever, #1282).
|
||||||
|
|
||||||
|
- Hold rendering only for animated content (asturksever, #1281).
|
||||||
|
- Exercise current vector road sources and published camera bounds in Traffic fixture QA (asturksever, #1281).
|
||||||
|
- Establish browser-level foreground visibility for Traffic QA and reject hidden-page samples (asturksever, #1281).
|
||||||
|
- Supply CORS headers on cross-origin traffic fixtures and report browser exceptions without disabling browser security (asturksever, #1281).
|
||||||
|
- Isolate the Traffic keyless browser scenario from the keyed page’s workers and intercepted cache (asturksever, #1281).
|
||||||
|
- Restore minimized test windows before screenshots so native compositor pauses cannot stall browser acceptance (asturksever, #1281).
|
||||||
|
- Intercept only Traffic provider URLs, leaving Cesium worker and module loading untouched (asturksever, #1281).
|
||||||
|
- Settle an unfinished Traffic height correction when the last animated point leaves, so the layer stops reporting loading once its render hold is released (asturksever, #1281).
|
||||||
|
|
||||||
|
- Throttle mission hover picks with a trailing check for the last pointer position, and clear stale cursors (asturksever, #1279).
|
||||||
|
|
||||||
|
- Reserve singleflight before disk cache reads (asturksever, #1278).
|
||||||
|
|
||||||
|
- Bound each height request to one upstream chunk (asturksever, #1276).
|
||||||
|
|
||||||
|
- Preserve enable during first-load replacement (asturksever, #1275).
|
||||||
|
- Release disabled weather camera listeners and make teardown resource leaks fail the browser probe (asturksever, #1275).
|
||||||
|
- Keep visible-page weather teardown probes focused through the browser testing API (asturksever, #1275).
|
||||||
|
|
||||||
|
- Scan the kml root delimiter in bounded time (asturksever, #1274).
|
||||||
|
|
||||||
|
- Retire unavailable history and keep local trails (asturksever, #1273).
|
||||||
|
|
||||||
|
- Normalize geocoder bias across the antimeridian (asturksever, #1272).
|
||||||
|
|
||||||
|
- Reject inherited style keys during restore (asturksever, #1271).
|
||||||
|
|
||||||
|
- Respect provider cooldowns and bound tle groups (asturksever, #1270).
|
||||||
|
|
||||||
|
- Coalesce refreshes and cool down opensky failures (asturksever, #1269).
|
||||||
|
|
||||||
|
- Normalize station tags and retain valid mixed rows; skipped malformed rows still report degraded coverage (asturksever, #1268).
|
||||||
|
- Correct Radio QA push-to-talk setup to target the mounted voice controller (asturksever, #1268).
|
||||||
|
|
||||||
|
- Honor Docker Compose trusted-peer and allowed-host settings from the shell
|
||||||
|
or project `.env`, and let `GEV_KEY_SETUP_TRUSTED_PEERS=none` disable gateway
|
||||||
|
access to Provider Settings before Vite starts (asturksever, #1265).
|
||||||
|
|
||||||
|
- Prevent malformed Overpass filters from blocking the local server, and reject
|
||||||
|
cross-site or excessive query requests before reading their bodies
|
||||||
|
(asturksever, #1264; fixes #1248).
|
||||||
|
- Release unread responses from the optional Google-token probe so keyless
|
||||||
|
browser validation can settle; the Overpass gate recognizes only that probe's
|
||||||
|
expected 404 as an unavailable capability (asturksever, #1264).
|
||||||
|
- Make tracking arrival checks respect the aircraft update cadence while checking
|
||||||
|
orientation on the first reveal, avoiding false failures at faster render
|
||||||
|
rates (asturksever, #1264).
|
||||||
|
|
||||||
|
- The terrain-heights bounds tests no longer read the checkout's
|
||||||
|
`.gev-cache/terrain-heights.json`. Each test gets a cache file of its own, so
|
||||||
|
`npm test` no longer fails after the dev server has cached terrain near
|
||||||
|
Austin (KnottyDyes, #1244).
|
||||||
|
|
||||||
|
- Replace the blocky JPEG sky around the globe with the 9,096 stars of the
|
||||||
|
Yale Bright Star Catalogue, drawn as sharp points where they are at the
|
||||||
|
scene time, over a soft Milky Way from ESO/S. Brunier (CC BY 4.0). The Sun
|
||||||
|
and Moon stay; the anime style hides the Milky Way. Assets: 296 kB (gekh,
|
||||||
|
#1243).
|
||||||
|
|
||||||
|
- `get_traffic_flow` flags stale cached TomTom tiles and reports the oldest
|
||||||
|
tile fetch time (`stale`, `oldest_tile_at`), with pending freshness metadata
|
||||||
|
released on failed or cancelled tile reads (quanticsoul4772, #1239).
|
||||||
|
|
||||||
|
- Add Taiwan's national freeway cameras from the Freeway Bureau, MOTC to CCTV:
|
||||||
|
~1,800 cameras read at runtime from the official MOTC 2.0 open-data table,
|
||||||
|
with on-demand snapshots and attribution. No API key is required; set
|
||||||
|
`CCTV_TAIWAN_ENABLED=0` to skip it (419vive, #1237).
|
||||||
|
|
||||||
|
- Require a geocoded containment anchor for ambiguous analyst region names,
|
||||||
|
preserve boundary provenance and cancellation safety, and retain exact-name
|
||||||
|
precedence for natural outlines (manjunath22466, #1235).
|
||||||
|
|
||||||
|
- Serve the camera catalog while it refreshes. An expired catalog made every
|
||||||
|
camera request wait for the slowest pack to reload; it is now answered at
|
||||||
|
once while one background refresh runs, so only the first load waits. Estonia's
|
||||||
|
slow Tarktee feeds get 30 s instead of 15 s, so the pack no longer drops out
|
||||||
|
on about a third of loads (quanticsoul4772, #1234).
|
||||||
|
|
||||||
|
- Harden the Tarktee (Estonia) DATEX2 camera catalog fetches: refuse redirects,
|
||||||
|
cancel both response streams when either response fails or overflows the 4 MB
|
||||||
|
byte cap, and deduplicate camera IDs before per-pack prioritization
|
||||||
|
(yaka0007, #1233).
|
||||||
|
|
||||||
|
- Skip bike-share stations with missing, blank or invalid coordinates instead
|
||||||
|
of placing them at coerced zero coordinates or accepting out-of-range values.
|
||||||
|
Explicit zero coordinates and numeric strings remain supported (Bortlesboat, #1232).
|
||||||
|
|
||||||
|
- OpenSky sign-in matches what OpenSky accepts since 2026-03-18. The server
|
||||||
|
now reads `OPENSKY_CREDENTIALS_FILE` (the `credentials.json` OpenSky's
|
||||||
|
account page downloads) on every platform, not only through the macOS
|
||||||
|
launcher, and no longer sends retired username/password Basic auth:
|
||||||
|
`OPENSKY_AUTH_MODE=basic` loads anonymously with one warning saying what to
|
||||||
|
set instead (quanticsoul4772, #1229).
|
||||||
|
- Credential-file warnings omit parser excerpts and filesystem error details;
|
||||||
|
the path example also works with the macOS launchers.
|
||||||
|
|
||||||
|
- Load Ontario 511 cameras again. 511on.ca's camera endpoint now requires a
|
||||||
|
free developer key; set `ONTARIO_511_API_KEY` to enable the pack, which is
|
||||||
|
otherwise skipped with one log line saying where to get a key
|
||||||
|
(quanticsoul4772, #1228).
|
||||||
|
|
||||||
|
- The first-run card can be closed on a phone or tablet. "ESC to dismiss" is
|
||||||
|
now a button that closes the card the same way ESC does; touch screens read
|
||||||
|
DISMISS, and phones no longer hide it, so choosing a mission is not the only
|
||||||
|
way out (KnottyDyes, #1226).
|
||||||
|
|
||||||
|
- Add continuous globe rotation with direction and speed controls and a globe-fit
|
||||||
|
check before starting (manjunath22466, #1225).
|
||||||
|
|
||||||
|
- Open God's Eye View from Pinokio in the system browser using the supported
|
||||||
|
`popout` menu option. The application's framing policy is unchanged
|
||||||
|
(NukeThemAII, #1222).
|
||||||
|
|
||||||
|
- Avoid starting or queuing a reply when the local channel rejects a typed OpenAI
|
||||||
|
command, while preserving an earlier accepted deferred input
|
||||||
|
(Manjunath, #1220).
|
||||||
|
|
||||||
|
- Keep NASA FIRMS fire and ALPR camera labels English in voice tool results
|
||||||
|
(`track_entity`, `get_entity_context`, pointer context); the voice card still
|
||||||
|
shows them in the interface language (daikaginza, #1207).
|
||||||
|
|
||||||
|
- Report actual outcomes when voice enables or disables CCTV through the shared
|
||||||
|
layer lifecycle, including failed and cancelled changes (manjunath22466,
|
||||||
|
#1205).
|
||||||
|
|
||||||
|
- Draw CCTV stills at their source aspect on the monitor plane instead of
|
||||||
|
stretching them to the 16:9 canvas. Most DOT cameras publish 4:3 frames, so
|
||||||
|
every still was widened; they are now centered and pillarboxed on the dark
|
||||||
|
the placeholder already ends on (Danielslay86, #1201).
|
||||||
|
|
||||||
|
- Color CCTV cameras with a video feed violet. Cameras with an HLS, MP4 or
|
||||||
|
WebM feed were the same cyan as stills, so a DelDOT or Statens vegvesen
|
||||||
|
video camera could only be found by opening it. Stills stay cyan and the
|
||||||
|
active camera stays amber, and focus de-emphasis fades the violet the same
|
||||||
|
way (Danielslay86, #1200).
|
||||||
|
|
||||||
|
- Keep Space Missions marked STALE when Launch Library 2 falls back to cached
|
||||||
|
launch data, and return to NOMINAL after a fresh response without discarding
|
||||||
|
retained missions (rudycelekli, #1199).
|
||||||
|
|
||||||
|
- Share Launch Library request cache keys across installations refreshing in the
|
||||||
|
same UTC minute, identify the app to the provider, and explain that standard
|
||||||
|
space-missions access needs no key. Optional higher-rate LL2 tokens are paid
|
||||||
|
upgrades; existing local cache and server-only token handling are unchanged
|
||||||
|
(rudycelekli, #1197).
|
||||||
|
|
||||||
|
- Add nine Spanish city bike systems to Bike Share and `get_bike_share`: BiciMAD
|
||||||
|
(Madrid), Valenbisi (Valencia), Bizi Zaragoza, Sevici (Seville), BIKI
|
||||||
|
(Valladolid), BiciCoruña, Dbizi (Donostia-San Sebastián), Bilbao Bizi and
|
||||||
|
BicinRivas, about 1,840 stations. Station names read underscores as word
|
||||||
|
breaks, and station capacity counts docks rather than PBSC's geofenced area
|
||||||
|
(ulihhhh, #1190).
|
||||||
|
|
||||||
|
- Keep missing launch-pad coordinates unknown so launches use the location
|
||||||
|
fallback or stay off the map instead of appearing at 0°N 0°E. Missing stage
|
||||||
|
flight numbers no longer show "FLIGHT 0"
|
||||||
|
(Rudy Celekli, @rudycelekli, #1186).
|
||||||
|
|
||||||
|
- Flights' ground floor works across the antimeridian. The viewer-distance
|
||||||
|
check behind the 150 km floor radius and its copy in the photoreal mesh-floor
|
||||||
|
sampler subtracted longitudes without wrapping, so two points 21 km apart
|
||||||
|
across 180° measured about 38,000 km and low or grounded aircraft just across
|
||||||
|
the line from the viewer were never floored. A contact's display corridor
|
||||||
|
walked a segment across 180° the long way round, spending its cell budget on
|
||||||
|
ground behind the contact, and a contact on the seam lost its cell hysteresis.
|
||||||
|
SEARCH NEARBY SITES on a view across 180° searched Google Places around
|
||||||
|
longitude 0, on the far side of the globe, with a radius sized from the view's
|
||||||
|
height alone; it now searches the view (Aditya, @Rohilalala, #1173).
|
||||||
|
|
||||||
|
- Keep missing CCTV numbers unknown instead of reading them as zero. `Number`
|
||||||
|
turns null, blank strings and booleans into 0 or 1, and the camera source
|
||||||
|
list sends an unknown number as JSON null, so a source-pack camera that left
|
||||||
|
out its pose reached the browser as zeros clamped to each field's floor —
|
||||||
|
facing north, pitch −2°, FOV 20°, range 120 m, mount 6 m and ground 0 m —
|
||||||
|
instead of the defaults (id-hash heading, −17°, 74°, 700 m, 24 m and the
|
||||||
|
city's ground); a null or blank position placed the camera at 0,0. The
|
||||||
|
server's `toFiniteNumber` and the browser's `safeNumber` now treat absent,
|
||||||
|
blank and boolean values as missing. The same conversion also stops a
|
||||||
|
Tallinn row with a null heading reading as a confident north-facing camera,
|
||||||
|
a Caltrans camera with a blank elevation losing its 150 m ground prior, and
|
||||||
|
a TfL or Catalonia camera with a null or blank coordinate being kept at 0,0
|
||||||
|
or on the zero meridian (omerbberk, #1172).
|
||||||
|
|
||||||
|
- Director shot actions land on the requested shot. A seek to a shot's start
|
||||||
|
could round a hair below it and select the end of the previous shot for some
|
||||||
|
shot counts and durations (Aditya, @Rohilalala, #1171).
|
||||||
|
|
||||||
|
- Fire-perimeter queries handle outlines and holes crossing the antimeridian:
|
||||||
|
areas enclosed near ±180° are included, areas at the opposite meridian are
|
||||||
|
excluded, and returned perimeter coordinates stay near the dateline. Incident
|
||||||
|
cards use the same circular longitude mean and compare multipart outline
|
||||||
|
areas along short longitude edges, keeping the card on the largest polygon
|
||||||
|
(Arianna Method, @ariannamethod, #1142).
|
||||||
|
|
||||||
|
- Scenes keep the "Scene not saved" status when a capture, update, append or
|
||||||
|
import cannot be saved, and an unreadable saved project no longer overwrites
|
||||||
|
the recovery checkpoint. `startScene()` resolves to its documented result,
|
||||||
|
`afterShotId` works without an explicit scene, and `scenesRun` counts the
|
||||||
|
distinct scenes queued (djabhi31, #1135).
|
||||||
|
|
||||||
|
- Label camera-tool Street View fallbacks as historical Google imagery rather
|
||||||
|
than the current camera view; keep unknown frame provenance explicit
|
||||||
|
(rudycelekli, #1118).
|
||||||
|
|
||||||
|
- Release superseded Google Street View open waits and suppress metadata
|
||||||
|
requests whose viewer was unmounted while the service loaded (rudycelekli,
|
||||||
|
#1117).
|
||||||
|
|
||||||
|
- Exclude aircraft without a reported altitude from requested altitude bands;
|
||||||
|
unfiltered aircraft queries still retain them (rudycelekli, #1115).
|
||||||
|
|
||||||
|
- A failed Esri Satellite or keyless terrain load still shows OSM or flat terrain
|
||||||
|
at once, but no longer lasts the whole session: while the fallback is on
|
||||||
|
screen, the real provider is retried in the background (30 seconds, doubling
|
||||||
|
to a 10-minute cap) and replaces it live once it loads. Map switches never
|
||||||
|
wait for a retry (Aditya, @Rohilalala, #1111).
|
||||||
|
|
||||||
|
- Skip TLE sets whose line 1 and line 2 carry different catalog numbers, so a
|
||||||
|
spliced catalog cannot pair one satellite's elements with another's; the next
|
||||||
|
valid set still parses (VG31OP, #1101).
|
||||||
|
|
||||||
|
- Overpass data whose element tags contain a failure phrase ("timed out",
|
||||||
|
"runtime error", "too many requests" and similar, e.g. a place named "Timed
|
||||||
|
Out Escape Rooms") is returned as data instead of being refused and putting
|
||||||
|
the endpoint on cooldown; upstream failures are read from the remark (Aditya,
|
||||||
|
@Rohilalala, #1097).
|
||||||
|
|
||||||
|
- Military aircraft keep their last heading and speed when an adsb.lol row omits
|
||||||
|
`track` or `gs`, instead of turning north and stopping; a grounded row without
|
||||||
|
a speed still reads 0 (Aditya, @Rohilalala, #1096).
|
||||||
|
|
||||||
|
- Update DOMPurify to 3.4.17, Sharp to 0.35.5 and source-map-js to 1.2.2 for
|
||||||
|
GHSA-p98j-92pf-mc4p, GHSA-6688-9rhm-gjv2, GHSA-wq5f-xc86-pv6w and
|
||||||
|
GHSA-68fv-2mgg-jv7q; `npm audit` now reports no vulnerabilities (NurstarK,
|
||||||
|
#1094).
|
||||||
|
|
||||||
|
- Location search accepts two-dimensional WGS84 `geo:` URIs offline, with
|
||||||
|
the existing coordinate framing and label. Unsupported URI fields are passed
|
||||||
|
to the remaining search providers unchanged (cj-vana, #1071).
|
||||||
|
|
||||||
|
- Keyless standalone startup answers an unavailable Google tile-token endpoint
|
||||||
|
with uncached 204 instead of a browser-visible 404. Existing providers take
|
||||||
|
precedence, and unrelated API routes behave as before. (dvilrgamerz, #1066)
|
||||||
|
|
||||||
|
- On Windows, `npm run doctor` and the Pinokio install no longer print Node's
|
||||||
|
DEP0190 warning ("Passing args to a child process with shell option true can
|
||||||
|
lead to security vulnerabilities"): npm.cmd's fixed subcommand is now part of
|
||||||
|
the shell command string instead of an argument array (quanticsoul4772, #1057).
|
||||||
|
|
||||||
|
- `aircraft_in_area` and `vessels_in_area` take `group_by` and `stats`, so an
|
||||||
|
MCP client can ask which airlines, countries, aircraft types or ship types
|
||||||
|
dominate an area, or the average altitude or speed, over every match instead
|
||||||
|
of estimating from the returned rows. Airlines come from the ICAO designator
|
||||||
|
in each callsign (SWA1234 counts as SWA), so the breakdown works on feeds
|
||||||
|
that report no operator; operator and type are counted only where the feed
|
||||||
|
reports them. Ship types count by family, so AIS codes 70 to 79 are all
|
||||||
|
cargo; type 0 ("not available") and the reserved codes 100 to 255 count as
|
||||||
|
unknown. Altitude and speed stats cover airborne aircraft only, so parked
|
||||||
|
aircraft do not pull the means down. Every vessel answer, `find_vessel`
|
||||||
|
included, now reads AIS ship-type codes as their family and a destination
|
||||||
|
with no letters as unknown (quanticsoul4772, #1056).
|
||||||
|
|
||||||
|
- Add Hong Kong Transport Department traffic snapshot cameras as a keyless CCTV
|
||||||
|
source pack: the public DATA.GOV.HK English locations XML, frames pinned to
|
||||||
|
`tdcctv.data.one.gov.hk`, with DATA.GOV.HK Terms of Use attribution. By
|
||||||
|
default nearly the full TD catalog (~1,013 cameras; cap 1,100) loads,
|
||||||
|
prioritized across Hong Kong Island, Kowloon and the New Territories. Headings
|
||||||
|
use the shared id-hash fallback at low confidence. `CCTV_HK_MAX_SOURCES` sets
|
||||||
|
the cap (8–1,200) and `CCTV_HK_ENABLED=0` turns the pack off (JCY2515, #990).
|
||||||
|
|
||||||
|
- Cross-site pages can no longer fill the terrain-height cache or spend the
|
||||||
|
visitor's own `/api/terrain/heights` rate-limit quota through the visitor's
|
||||||
|
browser. The route refuses a cross-site `Sec-Fetch-Site` or a foreign or
|
||||||
|
opaque `Origin` before rate limiting, cache access or upstream work. Unlike
|
||||||
|
the cost-bearing routes it still answers same-origin requests that arrive
|
||||||
|
through a reverse proxy, so terrain keeps rendering behind nginx, Caddy or
|
||||||
|
Cloudflare instead of falling back to the geoid (Sunil56224972, daikaginza,
|
||||||
|
#975).
|
||||||
|
|
||||||
|
- Street Level is rebuilt around one small provider registry: Mapillary,
|
||||||
|
KartaView, Panoramax and Google Street View share one coverage drawer and
|
||||||
|
one photo viewer, one source lit at a time. Floating and docked panels,
|
||||||
|
FOLLOW, surface modes, the legend and the Mapillary proxy's disk cache are
|
||||||
|
gone; `.gev-cache/mapillary/` from earlier runs is no longer read and can be
|
||||||
|
deleted (gekh, #974).
|
||||||
|
|
||||||
|
- Cross-site browser requests can no longer spend the TomTom traffic-tile
|
||||||
|
budget through `/api/tomtom/*`. The shared same-site gate runs before status,
|
||||||
|
cache, and upstream work; same-origin app requests and non-browser local
|
||||||
|
callers remain supported (Sunil56224972, #973).
|
||||||
|
|
||||||
|
- Recent Imagery: opening DETAILS scrolls its card into view when it opens below
|
||||||
|
the fold. The panel used to restore the previous scroll position right after
|
||||||
|
the reveal, so the card stayed out of sight (Ian Blenke, #970).
|
||||||
|
|
||||||
|
- Harden Vite's filesystem deny list for local-only artifacts: repo `dist/`,
|
||||||
|
`.gev-logs/`, and `.gev-cache/` are denied without blocking dependency
|
||||||
|
`dist/` directories, and checkout paths containing glob metacharacters are
|
||||||
|
escaped before building the root-anchored pattern (blowing-a-raspberry, #948).
|
||||||
|
|
||||||
|
- Give each Statens vegvesen camera its own ground height from Kartverket's
|
||||||
|
keyless point-height API (CC BY 4.0) instead of one flat 150 m prior, so
|
||||||
|
mountain-pass cameras no longer sit hundreds of metres below the terrain
|
||||||
|
where neither the point-height prior nor the ground snap resolves. About 850
|
||||||
|
of ~855 cameras get a height; the rest keep the old prior. Requests carry 20
|
||||||
|
points, the most the API answers from its 1 m terrain model, eight at a
|
||||||
|
time; a cold lookup takes about 6–13 s. Cameras over water use the water
|
||||||
|
surface rather than the sea-floor depth the API returns there; for bridge
|
||||||
|
cameras that is still below the deck. Heights are cached for good and misses
|
||||||
|
for 6 h, so a refresh only asks about new or moved cameras. After a
|
||||||
|
failing request (a 408, 429 or 5xx, a network error, a request over 10 s or
|
||||||
|
an unreadable body) the lookup stops and backs off (Retry-After, else
|
||||||
|
30 min, longer than the refresh). The lane waits at most 12 s from its
|
||||||
|
start, and only on a cold cache; a slower first lookup leaves the cameras it
|
||||||
|
had not reached on the flat prior until the next 15-minute refresh, and
|
||||||
|
keeps filling the cache meanwhile.
|
||||||
|
`CCTV_VEGVESEN_HEIGHTS=0` turns it off. Follow-up to #929 (trygvehhh, #945).
|
||||||
|
|
||||||
|
- Extract a reusable model-free World Packet from the existing voice
|
||||||
|
`get_current_view_state` owner without restoring disabled layers to the
|
||||||
|
model context. Keep the enabled-only payload, disabled-layer count, source
|
||||||
|
provenance, action result and view-state contract; read the layer roster once
|
||||||
|
per packet; voice retains the speech/card envelope at its own boundary.
|
||||||
|
Credits: original GEV voice/layer contributors and #880 implementation.
|
||||||
|
|
||||||
|
- Add optional Gemini Live voice to the standalone app, with a provider chooser,
|
||||||
|
server-held API key, microphone streaming, shared map actions and captions.
|
||||||
|
Switching providers ends the old conversation and preserves the input mode
|
||||||
|
(Manjunath, #872).
|
||||||
|
|
||||||
|
- Close the compact Radio disclosure when an adaptive tactical right-rail
|
||||||
|
restore directly expands Context, before Space Missions can be covered.
|
||||||
|
A repeated Context-open request also clears stale disclosure state.
|
||||||
|
Real PanelChrome constructor wiring and the rail reflow path are covered
|
||||||
|
by focused regressions. Credits: kvnloo (#816), raysung-2026's original
|
||||||
|
#804 report, asturksever's review, and quanticsoul4772's independent
|
||||||
|
Windows diagnosis.
|
||||||
|
|
||||||
|
- Changelog entries are now one file per pull request. A change adds
|
||||||
|
`changelog.d/<pull request>-<slug>.md` instead of editing the top of
|
||||||
|
`CHANGELOG.md`, which two pull requests in flight could not share — it was the
|
||||||
|
most frequent merge conflict in the open queue, and for six of them the only
|
||||||
|
one. `npm run changelog` splices the directory into `CHANGELOG.md` at release
|
||||||
|
time and empties it; `npm run changelog:check` reports the batch and changes
|
||||||
|
nothing. Fragment text is copied through unchanged (daikaginza, #787).
|
||||||
|
|
||||||
|
- Read satellite catalog numbers written in Alpha-5, the TLE spelling of
|
||||||
|
numbers above 99999: a letter for the first two digits, skipping I and O
|
||||||
|
(`A0000` is 100000, `Z9999` is 339999). The satellites layer converted such a
|
||||||
|
field to `NaN`, so every Alpha-5 satellite shared one catalog entry and all
|
||||||
|
but the first were dropped as duplicates; the satellite tools read the same
|
||||||
|
field as no number, so such a satellite could not be found by it. The layer
|
||||||
|
and the tools now decode the field in `src/sources/tle.js`, and reject a
|
||||||
|
malformed one (a sign, decimal point, hex, embedded space or stray letter)
|
||||||
|
instead of reading part of it as a number. This is Alpha-5 TLE
|
||||||
|
compatibility: CelesTrak's TLE feed carries no satellite above 99999, which
|
||||||
|
needs the OMM feed (#1191) (Honorius Neogy, #767, fixes #751).
|
||||||
|
|
||||||
|
- The dev server no longer watches `.gev-cache` and `.gev-logs`. Providers write
|
||||||
|
cache files and logs there while the app runs, and none of it is source, so
|
||||||
|
the watcher now skips it; this guards against the DNS-lookup delays heavy
|
||||||
|
cache writes have caused, though no difference was measured on current main
|
||||||
|
(monwiss, #761).
|
||||||
|
|
||||||
|
- Bound what a caller can grow through `/api/terrain/heights`. Every distinct
|
||||||
|
5-decimal point was a permanent in-memory entry, a permanent line in an
|
||||||
|
uncapped `.gev-cache/terrain-heights.json`, and a live upstream call, with
|
||||||
|
nothing limiting the rate. The point cache now holds at most 50,000 entries
|
||||||
|
and evicts oldest-first, a cache file above
|
||||||
|
24 MB is not read back, and the route is limited to 90 requests/minute per
|
||||||
|
client (270 global). Evicting a point costs one refetch, never a wrong
|
||||||
|
answer, since terrain does not move. Ordinary use is unaffected: a full
|
||||||
|
`test:track` run drives 10 requests, peaking at 8/min (daikaginza, #686).
|
||||||
|
|
||||||
|
- Add FL511 (Florida DOT) traffic cameras as an opt-in CCTV source pack:
|
||||||
|
the statewide ArcGIS catalog, read a page at a time under a byte cap with
|
||||||
|
redirects refused, and frames pinned to FL511's two image hosts over HTTPS.
|
||||||
|
FL511's terms limit its content to individual, personal use, and any other
|
||||||
|
use (a public deployment included) needs FDOT's express written consent, so
|
||||||
|
the pack stays off unless `CCTV_FLORIDA_ENABLED=1`. Headings come from the
|
||||||
|
catalog's direction codes, and camera ids come from the image channel, since
|
||||||
|
the catalog's ID column repeats across regions. `CCTV_FLORIDA_MAX_SOURCES`
|
||||||
|
sets the cap (Danielslay86, #605).
|
||||||
|
|
||||||
|
- The client's great-circle distances now come from one shared
|
||||||
|
`src/geoDistance.js` (`greatCircleKm`/`greatCircleMeters`, `{lat, lon}`
|
||||||
|
points). The Earth radius is standardized on 6371.0088 km, so distances shift
|
||||||
|
by about 1.4 ppm and region areas by about 2.8 ppm, and missing or
|
||||||
|
out-of-range points return `Infinity` (onionviolet, #287; Rajojha-1, #749).
|
||||||
|
|
||||||
- The tracked-aircraft descriptor now publishes `verticalRateMps`, on both the
|
- The tracked-aircraft descriptor now publishes `verticalRateMps`, on both the
|
||||||
civil and military layers, so a consumer reading `getTrackedInfo()` no longer
|
civil and military layers, so a consumer reading `getTrackedInfo()` no longer
|
||||||
has to reach past it into the raw poll record for climb/descent. Reported,
|
has to reach past it into the raw poll record for climb/descent. Reported,
|
||||||
never derived: an aircraft that has not transmitted a rate reads as `null`
|
never derived: an aircraft that has not transmitted a rate reads as `null`
|
||||||
rather than a level-flight `0`.
|
rather than a level-flight `0`.
|
||||||
|
|
||||||
|
- Bound how often the GBFS proxy will relay. `/api/gbfs` already restricted
|
||||||
|
where it could go — https, an allowlisted host, a station feed path, no
|
||||||
|
redirects, a 12-second deadline and a 5 MB body cap — but nothing restricted
|
||||||
|
how often, so the dev server would make unlimited requests at a third-party
|
||||||
|
bikeshare operator on any caller's behalf. The route now admits 120
|
||||||
|
requests/minute per client address, refusing the rest with 429 and
|
||||||
|
`Retry-After` before any outbound fetch. The bikeshare layer needs about 15 a
|
||||||
|
minute with every catalogued system in range, so ordinary use is unchanged.
|
||||||
|
|
||||||
- Add nine Ontario transit feeds: TTC, MiWay, Hamilton HSR, Durham Region,
|
- Add nine Ontario transit feeds: TTC, MiWay, Hamilton HSR, Durham Region,
|
||||||
YRT/Viva, Guelph, Burlington, Barrie and Kingston, each carrying its own
|
YRT/Viva, Guelph, Burlington, Barrie and Kingston, each carrying its own
|
||||||
licence quote and attribution. A feed can now name its own modes, so Toronto's
|
licence quote and attribution. A feed can now name its own modes, so Toronto's
|
||||||
@@ -282,8 +978,6 @@
|
|||||||
point. A slow render gets one bounded second frame request, while hidden,
|
point. A slow render gets one bounded second frame request, while hidden,
|
||||||
stale, moved-camera, invalid and oversized captures still fail closed.
|
stale, moved-camera, invalid and oversized captures still fail closed.
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
- Add a keyless Air quality layer to the Weather group, with Environment and
|
- Add a keyless Air quality layer to the Weather group, with Environment and
|
||||||
Climate Change Canada's AQHI as the first registered provider. Readings carry
|
Climate Change Canada's AQHI as the first registered provider. Readings carry
|
||||||
`{ provider, scale, value, band }` so another network can be added without
|
`{ provider, scale, value, band }` so another network can be added without
|
||||||
@@ -498,6 +1192,28 @@
|
|||||||
needs only the Map Tiles API and the Maps JavaScript API (Street View). Keyless sessions and the Photon/Nominatim
|
needs only the Map Tiles API and the Maps JavaScript API (Street View). Keyless sessions and the Photon/Nominatim
|
||||||
fallback order are unchanged.
|
fallback order are unchanged.
|
||||||
|
|
||||||
|
- Voice can switch on every shipped data layer (the two scene-driven
|
||||||
|
Bhote Koshi layers stay scene-only), including transit, Recent
|
||||||
|
Imagery, wind, the weather layers, cyclones and mapped installations, and
|
||||||
|
understands their spoken names ("buses", "hurricanes", "military bases").
|
||||||
|
One voice manifest (`src/voice/layerManifest.js`) generates the tool enums
|
||||||
|
and aliases, and a unit test fails when a new layer has no entry.
|
||||||
|
- `analyst_query` counts up to 250,000 loaded records per layer instead of the
|
||||||
|
first 2,000, and says when a layer went past that (`complete: false`). It
|
||||||
|
also answers over bikeshare stations, transit vehicles, rocket launches,
|
||||||
|
cyclones, ALPR cameras, mapped installations, local ADS-B aircraft and fire perimeters; fire
|
||||||
|
records carry their sensor (VIIRS or MODIS).
|
||||||
|
- Unknown fields, operators, units, value types, scope kinds and invalid
|
||||||
|
centres are refused with the allowed values instead of answering zero or widening to anywhere. A
|
||||||
|
layer that is off, still loading or unavailable is reported as such, not
|
||||||
|
as a count of zero; a partly answerable query is marked `partial`.
|
||||||
|
Distance sorts honour `sortDir`, a written unit such as `altitudeM(ft)`
|
||||||
|
converts, and large rankings no longer sort the whole set.
|
||||||
|
- Results carry `lat`/`lon` for each item and a `display` object with the
|
||||||
|
scope, the feed window (earthquakes and fires cover the last 24 h) and any
|
||||||
|
caveat for the screen. The model is asked to answer in one short phrase.
|
||||||
|
- The weather and Recent Imagery panels can be opened by voice.
|
||||||
|
|
||||||
## [0.2.1] — 2026-10-02 — God's Eye View through Your Agent (with security fixes and other features)
|
## [0.2.1] — 2026-10-02 — God's Eye View through Your Agent (with security fixes and other features)
|
||||||
|
|
||||||
- Ship each bundled data pack once. The region, marine, admin-boundary,
|
- Ship each bundled data pack once. The region, marine, admin-boundary,
|
||||||
@@ -780,32 +1496,6 @@
|
|||||||
(`src/ui/imagerySplit.js`, `src/maps/imageryComparison.js`), and
|
(`src/ui/imagerySplit.js`, `src/maps/imageryComparison.js`), and
|
||||||
`MapSourceController.subscribe()` reports every settled map switch.
|
`MapSourceController.subscribe()` reports every settled map switch.
|
||||||
|
|
||||||
## Unreleased — voice reaches every layer; honest analyst answers
|
|
||||||
|
|
||||||
- Voice can switch on every shipped data layer (the two scene-driven
|
|
||||||
Bhote Koshi layers stay scene-only), including transit, Recent
|
|
||||||
Imagery, wind, the weather layers, cyclones and mapped installations, and
|
|
||||||
understands their spoken names ("buses", "hurricanes", "military bases").
|
|
||||||
One voice manifest (`src/voice/layerManifest.js`) generates the tool enums
|
|
||||||
and aliases, and a unit test fails when a new layer has no entry.
|
|
||||||
- `analyst_query` counts up to 250,000 loaded records per layer instead of the
|
|
||||||
first 2,000, and says when a layer went past that (`complete: false`). It
|
|
||||||
also answers over bikeshare stations, transit vehicles, rocket launches,
|
|
||||||
cyclones, ALPR cameras, mapped installations, local ADS-B aircraft and fire perimeters; fire
|
|
||||||
records carry their sensor (VIIRS or MODIS).
|
|
||||||
- Unknown fields, operators, units, value types, scope kinds and invalid
|
|
||||||
centres are refused with the allowed values instead of answering zero or widening to anywhere. A
|
|
||||||
layer that is off, still loading or unavailable is reported as such, not
|
|
||||||
as a count of zero; a partly answerable query is marked `partial`.
|
|
||||||
Distance sorts honour `sortDir`, a written unit such as `altitudeM(ft)`
|
|
||||||
converts, and large rankings no longer sort the whole set.
|
|
||||||
- Results carry `lat`/`lon` for each item and a `display` object with the
|
|
||||||
scope, the feed window (earthquakes and fires cover the last 24 h) and any
|
|
||||||
caveat for the screen. The model is asked to answer in one short phrase.
|
|
||||||
- The weather and Recent Imagery panels can be opened by voice.
|
|
||||||
|
|
||||||
## Unreleased — local receiver feeds
|
|
||||||
|
|
||||||
### Local receiver feeds
|
### Local receiver feeds
|
||||||
|
|
||||||
- The Local ADS-B layer also reads local 1090 MHz and 978 MHz UAT decoder
|
- The Local ADS-B layer also reads local 1090 MHz and 978 MHz UAT decoder
|
||||||
@@ -948,7 +1638,6 @@
|
|||||||
Add feed provenance to analyst/view answers and HUD context while retaining existing response fields and runner ownership (Matt Van Horn, #347).
|
Add feed provenance to analyst/view answers and HUD context while retaining existing response fields and runner ownership (Matt Van Horn, #347).
|
||||||
|
|
||||||
Analyst records for loaded satellites, datacenters and dams, with explicit bounded count/rank coverage (Matt Van Horn, #351).
|
Analyst records for loaded satellites, datacenters and dams, with explicit bounded count/rank coverage (Matt Van Horn, #351).
|
||||||
|
|
||||||
- New Fire Perimeters layer (Events group): live NIFC WFIGS interagency
|
- New Fire Perimeters layer (Events group): live NIFC WFIGS interagency
|
||||||
wildfire incident perimeters as ground-clamped polygons with a
|
wildfire incident perimeters as ground-clamped polygons with a
|
||||||
containment-colored fire line, refreshed every 5 minutes from the public
|
containment-colored fire line, refreshed every 5 minutes from the public
|
||||||
@@ -1225,6 +1914,7 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md
|
|||||||
|
|
||||||
### Other changes
|
### Other changes
|
||||||
|
|
||||||
|
|
||||||
- Add ECMWF IFS model selection to Wind (#464, thanks @beneduzi), with model-scoped forecast-step caches, cancellation of replaced requests, and separate issue/valid timestamps.
|
- Add ECMWF IFS model selection to Wind (#464, thanks @beneduzi), with model-scoped forecast-step caches, cancellation of replaced requests, and separate issue/valid timestamps.
|
||||||
|
|
||||||
- Add bounded Director feature actions with accessible controls, explicit camera/layer admission and cancellation; restore pack geometry on same-shot seek. Preserve existing scenes and content attribution.
|
- Add bounded Director feature actions with accessible controls, explicit camera/layer admission and cancellation; restore pack geometry on same-shot seek. Preserve existing scenes and content attribution.
|
||||||
@@ -2033,3 +2723,4 @@ represent previously published GitHub Releases.
|
|||||||
## [0.1.0] — 2026-02-09
|
## [0.1.0] — 2026-02-09
|
||||||
|
|
||||||
- Initial project version.
|
- Initial project version.
|
||||||
|
|
||||||
+28
-1
@@ -108,7 +108,7 @@ prints anything; never fix that by renaming or deleting PR A's published token.
|
|||||||
The highest-leverage places to jump in:
|
The highest-leverage places to jump in:
|
||||||
|
|
||||||
- **🌆 Add a CCTV source pack.** Austin is the reference camera source. Adding another city means a clean public camera catalog with coordinates, attribution, and server-registered frame URLs (the proxy only fetches registered URLs — never client-supplied ones, see [SECURITY.md](SECURITY.md)). City packs are the best first lane.
|
- **🌆 Add a CCTV source pack.** Austin is the reference camera source. Adding another city means a clean public camera catalog with coordinates, attribution, and server-registered frame URLs (the proxy only fetches registered URLs — never client-supplied ones, see [SECURITY.md](SECURITY.md)). City packs are the best first lane.
|
||||||
- **🛰️ Add or improve a data layer.** Layer factories live in `src/layers/<family>/`, with source, record, controller and renderer owners implementing the layer interface (`init/enable/disable/update/destroy/getStats`, optional `getDetectableObjects`/`getStats`). Use an existing layer as a template.
|
- **🛰️ Add or improve a data layer.** Layer factories live in `src/layers/<family>/`, with source, record, controller and renderer owners implementing the layer interface (`init/enable/disable/update/destroy/getStats`, optional `getDetectableObjects`/`getStats`). Use an existing layer as a template, and see [Adding a new layer](#adding-a-new-layer) for what the PR needs.
|
||||||
- **🎙️ Extend voice control.** Voice arguments are defined in `src/voice/actionSchemas.js`, with server-side descriptions in `server/providers/openai/tools.js` and client-side execution in `src/voice/gevActions.js`. Keep the tool surface tight and the responses honest (confirm only what actually happened).
|
- **🎙️ Extend voice control.** Voice arguments are defined in `src/voice/actionSchemas.js`, with server-side descriptions in `server/providers/openai/tools.js` and client-side execution in `src/voice/gevActions.js`. Keep the tool surface tight and the responses honest (confirm only what actually happened).
|
||||||
- **🎨 Add a visual style.** Styles are GLSL post-process shaders in `src/styles/`.
|
- **🎨 Add a visual style.** Styles are GLSL post-process shaders in `src/styles/`.
|
||||||
- **🐛 Fix bugs / improve the first-run experience.** See [docs/KNOWN-ISSUES.md](docs/KNOWN-ISSUES.md).
|
- **🐛 Fix bugs / improve the first-run experience.** See [docs/KNOWN-ISSUES.md](docs/KNOWN-ISSUES.md).
|
||||||
@@ -153,6 +153,33 @@ ownership and adoption process.
|
|||||||
3. If you change runtime behavior, update `docs/CURRENT-STATE.md` in the same PR, and add your changelog entry as `changelog.d/<pr-number>-<slug>.md` instead of editing `CHANGELOG.md` — see [changelog.d/README.md](changelog.d/README.md). Distinct filenames don't conflict; a release assembles the directory into `CHANGELOG.md`. A documentation claim that needs a regression test should assert against `docs/CURRENT-STATE.md` or `README.md`, which a PR still edits directly.
|
3. If you change runtime behavior, update `docs/CURRENT-STATE.md` in the same PR, and add your changelog entry as `changelog.d/<pr-number>-<slug>.md` instead of editing `CHANGELOG.md` — see [changelog.d/README.md](changelog.d/README.md). Distinct filenames don't conflict; a release assembles the directory into `CHANGELOG.md`. A documentation claim that needs a regression test should assert against `docs/CURRENT-STATE.md` or `README.md`, which a PR still edits directly.
|
||||||
4. If you add or change a data source, update [DATA_SOURCES.md](DATA_SOURCES.md) with its license and attribution. **Don't add data you don't have the right to redistribute** — fetch it at runtime instead.
|
4. If you add or change a data source, update [DATA_SOURCES.md](DATA_SOURCES.md) with its license and attribution. **Don't add data you don't have the right to redistribute** — fetch it at runtime instead.
|
||||||
5. Describe what you changed and how you verified it (screenshots welcome for anything visual).
|
5. Describe what you changed and how you verified it (screenshots welcome for anything visual).
|
||||||
|
6. If you add a new layer, fill in the new-layer section of the PR template. See [Adding a new layer](#adding-a-new-layer).
|
||||||
|
|
||||||
|
## Adding a new layer
|
||||||
|
|
||||||
|
A new layer changes what everyone sees on the globe, so maintainers need to see
|
||||||
|
it working and know where its data comes from before reviewing the code. PRs
|
||||||
|
that add a layer must include all four of these in the description. The PR
|
||||||
|
template has a section for them.
|
||||||
|
|
||||||
|
1. **Screenshots.** At least one of the layer on the globe, plus its info card
|
||||||
|
or panel if it has one.
|
||||||
|
2. **A screen recording**, preferably 15 to 30 seconds: turn the layer on, move
|
||||||
|
around, and open a record. Drag an `.mp4` or `.mov` into the PR description.
|
||||||
|
GitHub accepts videos up to 10 MB on free accounts, so link to a larger file
|
||||||
|
instead of attaching it.
|
||||||
|
3. **The proposed category.** Say which layer-panel group the layer belongs in.
|
||||||
|
The groups are defined in `PANEL_GROUPS` in `src/ui/layerPanel.js` (today:
|
||||||
|
Movement, Cameras, Infrastructure, Events, Weather and Utilities). A layer
|
||||||
|
that isn't listed there falls under "Other layers". If none of the groups
|
||||||
|
fits, propose a new one and explain why.
|
||||||
|
4. **The data source and licence.** Name the provider and endpoint, link its
|
||||||
|
licence or terms, give the attribution shown in the app, and say whether it
|
||||||
|
needs a key and whether the data is fetched at runtime or bundled. Add the
|
||||||
|
same details to [DATA_SOURCES.md](DATA_SOURCES.md) and register the credit
|
||||||
|
in `src/data/dataCredits.js`.
|
||||||
|
|
||||||
|
Maintainers may hold a layer PR until all four are there.
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ How to read this:
|
|||||||
| **Environment and Climate Change Canada** (MSC GeoMet OGC API) | Air quality (AQHI) — Canadian station observations | [ECCC Data Servers End-use Licence](https://eccc-msc.github.io/open-data/licence/readme_en/) (open, attribution required, no endorsement implied) | "Contains information licensed under the Environment and Climate Change Canada Data Servers End-use Licence" |
|
| **Environment and Climate Change Canada** (MSC GeoMet OGC API) | Air quality (AQHI) — Canadian station observations | [ECCC Data Servers End-use Licence](https://eccc-msc.github.io/open-data/licence/readme_en/) (open, attribution required, no endorsement implied) | "Contains information licensed under the Environment and Climate Change Canada Data Servers End-use Licence" |
|
||||||
| **NASA GIBS** (WMTS tiles + Worldview Snapshots) and **NASA CMR** (granule catalog) | Recent Imagery layer: HLS S30/L30 (Sentinel-2, Landsat 8/9) and VIIRS true-colour tiles draped on the map, per-day thumbnails and PNG exports; the HLS granule catalog (dates, cloud, footprints) for a selected box | U.S. public domain (NASA data); keyless, browser-direct (CORS `*`); no bulk use — one 30-day catalog query per box, tiles bounded to the box, six tile requests in flight | "We acknowledge the use of imagery provided by services from NASA's Global Imagery Browse Services (GIBS), part of NASA's Earth Science Data and Information System (ESDIS)." — [gibs.earthdata.nasa.gov](https://gibs.earthdata.nasa.gov), [HLS](https://lpdaac.usgs.gov/products/hlss30v002/) |
|
| **NASA GIBS** (WMTS tiles + Worldview Snapshots) and **NASA CMR** (granule catalog) | Recent Imagery layer: HLS S30/L30 (Sentinel-2, Landsat 8/9) and VIIRS true-colour tiles draped on the map, per-day thumbnails and PNG exports; the HLS granule catalog (dates, cloud, footprints) for a selected box | U.S. public domain (NASA data); keyless, browser-direct (CORS `*`); no bulk use — one 30-day catalog query per box, tiles bounded to the box, six tile requests in flight | "We acknowledge the use of imagery provided by services from NASA's Global Imagery Browse Services (GIBS), part of NASA's Earth Science Data and Information System (ESDIS)." — [gibs.earthdata.nasa.gov](https://gibs.earthdata.nasa.gov), [HLS](https://lpdaac.usgs.gov/products/hlss30v002/) |
|
||||||
| **NIFC WFIGS** (Wildland Fire Interagency Geospatial Services, ArcGIS feature service) | Fire Perimeters layer: current interagency wildfire incident perimeters, containment, and incident facts | U.S. public domain (interagency wildland-fire data published through the [NIFC Open Data portal](https://data-nifc.opendata.arcgis.com/)); fetched keyless through `/api/fire-perimeters` (5-minute server cache) with `exceededTransferLimit` paging | "Wildfire perimeters: National Interagency Fire Center (WFIGS)" |
|
| **NIFC WFIGS** (Wildland Fire Interagency Geospatial Services, ArcGIS feature service) | Fire Perimeters layer: current interagency wildfire incident perimeters, containment, and incident facts | U.S. public domain (interagency wildland-fire data published through the [NIFC Open Data portal](https://data-nifc.opendata.arcgis.com/)); fetched keyless through `/api/fire-perimeters` (5-minute server cache) with `exceededTransferLimit` paging | "Wildfire perimeters: National Interagency Fire Center (WFIGS)" |
|
||||||
|
| **S2 Underground GhostMaps** (`github.com/s2underground/GhostMaps`) | S2 GhostMaps layer: the current edition of S2's KMZ intel products (Common Intelligence Picture, 30-day kinetic pull, Border Crisis Map, Iran and Yemen products, newest incident drop). Keyless. `/api/ghostmaps` reads the branch head from git's ref advertisement every 15 s (`GHOSTMAPS_POLL_SECONDS`, minimum 5 s), lists the tree through the GitHub REST API only when the head changes (optional `GITHUB_TOKEN`), downloads files by commit-pinned raw URLs into `.gev-cache/ghostmaps/` and pushes new catalogs to browsers over server-sent events | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) (repository README: "S2 Underground © 2025 by S2A1"); non-commercial, attribution required; files are fetched at runtime, never stored in this repository | "S2 GhostMaps intel products: S2 Underground © by S2A1, CC BY-NC-SA 4.0" — shown in-app |
|
||||||
| **InciWeb** (inciweb.wildfire.gov) | Per-incident "InciWeb ↗" card links to official incident pages (narratives, evacuation/closure notices) | U.S. government public incident information. Links are matched best-effort by incident name and state, with incident page origin and update times checked on selection. Same-origin `/api/fire-perimeters/inciweb` routes cache the catalog for 1 hour and publication metadata for 30 minutes; linked pages remain InciWeb content | "Incident information: InciWeb (inciweb.wildfire.gov)" |
|
| **InciWeb** (inciweb.wildfire.gov) | Per-incident "InciWeb ↗" card links to official incident pages (narratives, evacuation/closure notices) | U.S. government public incident information. Links are matched best-effort by incident name and state, with incident page origin and update times checked on selection. Same-origin `/api/fire-perimeters/inciweb` routes cache the catalog for 1 hour and publication metadata for 30 minutes; linked pages remain InciWeb content | "Incident information: InciWeb (inciweb.wildfire.gov)" |
|
||||||
| **OpenFreeMap / OpenMapTiles** | Vector tiles | OpenStreetMap data under ODbL 1.0 | OpenFreeMap © OpenMapTiles |
|
| **OpenFreeMap / OpenMapTiles** | Vector tiles | OpenStreetMap data under ODbL 1.0 | OpenFreeMap © OpenMapTiles |
|
||||||
| **Overture Maps Foundation** | Military area names pack | [ODbL 1.0](https://opendatacommons.org/licenses/odbl/1-0/) | Overture Maps Foundation |
|
| **Overture Maps Foundation** | Military area names pack | [ODbL 1.0](https://opendatacommons.org/licenses/odbl/1-0/) | Overture Maps Foundation |
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ COPY --chown=node:node . .
|
|||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
ENV PORT=4173
|
ENV PORT=4173
|
||||||
EXPOSE 4173
|
EXPOSE 4173
|
||||||
|
# node:slim has no curl, so probe with Node's built-in fetch. 127.0.0.1 is
|
||||||
|
# always on the Host allowlist, and the generous start period covers Vite's
|
||||||
|
# first-run dependency optimisation.
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||||
|
CMD ["node", "-e", "fetch('http://127.0.0.1:'+(Number.parseInt(process.env.PORT,10)||4173)+'/').then((r)=>process.exit(r.ok?0:1),()=>process.exit(1))"]
|
||||||
# The launcher is `npm run dev` plus one step: it trusts the container's
|
# The launcher is `npm run dev` plus one step: it trusts the container's
|
||||||
# gateway address for Provider Settings, because that is where Docker delivers
|
# gateway address for Provider Settings, because that is where Docker delivers
|
||||||
# the host's own browser from (never 127.0.0.1). See scripts/docker-start.mjs.
|
# the host's own browser from (never 127.0.0.1). See scripts/docker-start.mjs.
|
||||||
|
|||||||
@@ -111,13 +111,15 @@ app's **POWER UP** panel; [Keys & Costs](#-api-keys) explains the options.
|
|||||||
|
|
||||||
Available on **Windows, macOS, and Linux**. The Pinokio maintainer reports
|
Available on **Windows, macOS, and Linux**. The Pinokio maintainer reports
|
||||||
cross-platform testing of the fixed installer. The launcher installs the
|
cross-platform testing of the fixed installer. The launcher installs the
|
||||||
locked dependencies, finds a free local port, and opens the app.
|
locked dependencies, finds a free local port, and opens the app in your system
|
||||||
|
browser. The Server menu item keeps the launcher console available.
|
||||||
|
|
||||||
**Tried before and installation failed?** Update Pinokio and try again.
|
**Tried before and installation failed?** Update Pinokio and try again.
|
||||||
Version 8.2 fixes the launcher installation issue;
|
Version 8.2 fixes the launcher installation issue;
|
||||||
[details from the Pinokio maintainer](https://pinokio.co/posts/01m1m4p9xxm3qw7dnnpj2wr93g).
|
[details from the Pinokio maintainer](https://pinokio.co/posts/01m1m4p9xxm3qw7dnnpj2wr93g).
|
||||||
|
|
||||||
**Pinokio opens but the globe is very choppy?** Before treating that as a GPU
|
**Using an older launcher or opening the embedded view manually?** If the globe
|
||||||
|
is very choppy, before treating that as a GPU
|
||||||
compatibility or minimum-hardware problem, copy the local server URL shown by
|
compatibility or minimum-hardware problem, copy the local server URL shown by
|
||||||
Pinokio and open the same URL in your normal hardware-accelerated browser. One
|
Pinokio and open the same URL in your normal hardware-accelerated browser. One
|
||||||
Pinokio 8.2.0 user reported severe stutter in Pinokio's embedded view that went
|
Pinokio 8.2.0 user reported severe stutter in Pinokio's embedded view that went
|
||||||
@@ -207,6 +209,8 @@ env UID=1026 GID=100 docker compose up -d
|
|||||||
|
|
||||||
Keep the `env`: bash treats `UID` as read-only, so a plain `UID=1026` in front of a command is silently ignored. If the container can't write the checkout, it says so at start. Docker Desktop on macOS and Windows maps ownership for you.
|
Keep the `env`: bash treats `UID` as read-only, so a plain `UID=1026` in front of a command is silently ignored. If the container can't write the checkout, it says so at start. Docker Desktop on macOS and Windows maps ownership for you.
|
||||||
|
|
||||||
|
On SELinux hosts (Fedora, RHEL), compose mounts the checkout with `:z` so the container may read it; Docker may relabel the checkout for container access, which can take a moment on a large checkout. Without that, the start fails with `Cannot find module '/app/scripts/docker-start.mjs'`.
|
||||||
|
|
||||||
Behind a reverse proxy, put your keys in `.env` or pass them as environment variables - Provider Settings refuses proxied requests on purpose - and add the hostname you serve it on, for example `GEV_ALLOWED_HOSTS=globe.example.lan` (see [Sharing an instance](#-sharing-an-instance)).
|
Behind a reverse proxy, put your keys in `.env` or pass them as environment variables - Provider Settings refuses proxied requests on purpose - and add the hostname you serve it on, for example `GEV_ALLOWED_HOSTS=globe.example.lan` (see [Sharing an instance](#-sharing-an-instance)).
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
God's Eye View is a local-first client for **public** data. It is built for exploration, demos, and learning — not as a hardened production service. This document explains the security model so you can run it safely and report issues responsibly.
|
God's Eye View is a local-first client for **public** data. It is built for exploration, demos, and learning — not as a hardened production service. This document explains the security model so you can run it safely and report issues responsibly.
|
||||||
|
|
||||||
|
Vite's development file server also denies `.gev-logs/`, `.gev-cache/`, and
|
||||||
|
the package checkout's root `dist/` directory. Dependency `dist/` directories
|
||||||
|
remain available. This filesystem deny list does not filter `public/` assets or
|
||||||
|
preview output; never put private artifacts in a public asset directory.
|
||||||
|
|
||||||
## Reporting a vulnerability
|
## Reporting a vulnerability
|
||||||
|
|
||||||
Please report security issues **privately** — do not open a public issue for anything exploitable.
|
Please report security issues **privately** — do not open a public issue for anything exploitable.
|
||||||
|
|||||||
+33
-1
@@ -1,9 +1,29 @@
|
|||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
import { applicationHtmlPlugin } from './application-html.js';
|
import { applicationHtmlPlugin } from './application-html.js';
|
||||||
import cesium from 'vite-plugin-cesium';
|
import cesium from 'vite-plugin-cesium';
|
||||||
import { DEFAULT_ALLOWED_HOSTS, hostCheckPlugin } from './allowedHosts.js';
|
import { DEFAULT_ALLOWED_HOSTS, hostCheckPlugin } from './allowedHosts.js';
|
||||||
import { embedFramingPlugin } from './embed-framing.js';
|
import { embedFramingPlugin } from './embed-framing.js';
|
||||||
import { panelBuildPlugin } from './panel.js';
|
import { panelBuildPlugin } from './panel.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository root (this file lives at `<root>/build/vite.js`), as a
|
||||||
|
* forward-slash path for glob matching. Vite's `fs.deny` patterns are
|
||||||
|
* matched against the full absolute file path, and a bare `**\/dist/**`
|
||||||
|
* matches a `dist` directory at *any* depth — including dependencies that
|
||||||
|
* ship their own, such as `node_modules/vite/dist/client/client.mjs` (the
|
||||||
|
* dev client itself). Anchoring to this repository's root denies only the
|
||||||
|
* project's own build output.
|
||||||
|
*/
|
||||||
|
const REPO_ROOT = fileURLToPath(new URL('..', import.meta.url))
|
||||||
|
.replace(/\\/g, '/')
|
||||||
|
.replace(/\/$/, '');
|
||||||
|
|
||||||
|
export function escapeFsDenyPath(path) {
|
||||||
|
return path.replace(/[()[\]{}*?!+@|]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
const REPO_ROOT_GLOB = escapeFsDenyPath(REPO_ROOT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content-Security-Policy for every document the dev/preview server serves.
|
* Content-Security-Policy for every document the dev/preview server serves.
|
||||||
* No inline script is permitted, and the only foreign script origins are the
|
* No inline script is permitted, and the only foreign script origins are the
|
||||||
@@ -165,7 +185,19 @@ export function createBrowserViteConfig({
|
|||||||
// build/allowedHosts.js); IP addresses are always accepted.
|
// build/allowedHosts.js); IP addresses are always accepted.
|
||||||
allowedHosts: [...allowedHosts],
|
allowedHosts: [...allowedHosts],
|
||||||
fs: {
|
fs: {
|
||||||
deny: ['.env', '.env.*', '*.{crt,pem}', '**/.git/**', '**/ENVIRONMENT'],
|
deny: [
|
||||||
|
'.env',
|
||||||
|
'.env.*',
|
||||||
|
'*.{crt,pem}',
|
||||||
|
'**/.git/**',
|
||||||
|
'**/ENVIRONMENT',
|
||||||
|
'**/.gev-logs/**',
|
||||||
|
'**/.gev-cache/**',
|
||||||
|
// Root-anchored: a bare '**/dist/**' would also deny every
|
||||||
|
// dependency's own dist/ (e.g. node_modules/vite/dist/client),
|
||||||
|
// breaking /@vite/client and other dev-client assets.
|
||||||
|
`${REPO_ROOT_GLOB}/dist/**`,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
// These headers protect the document containing Provider Settings and
|
// These headers protect the document containing Provider Settings and
|
||||||
// give the whole page a real Content-Security-Policy (BROWSER_CSP).
|
// give the whole page a real Content-Security-Policy (BROWSER_CSP).
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- Director clock listeners, interaction observers and scene release failures
|
||||||
|
no longer leave the director inconsistent: each scene is released once, and
|
||||||
|
a phase error is kept (with the release failure logged) when cleanup also
|
||||||
|
fails (deepeshbind, #1014).
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
- `aircraft_in_area` and `vessels_in_area` take `group_by` and `stats`, so an
|
|
||||||
MCP client can ask which airlines, countries, aircraft types or ship types
|
|
||||||
dominate an area, or the average altitude or speed, over every match instead
|
|
||||||
of estimating from the returned rows. Airlines come from the ICAO designator
|
|
||||||
in each callsign (SWA1234 counts as SWA), so the breakdown works on feeds
|
|
||||||
that report no operator; operator and type are counted only where the feed
|
|
||||||
reports them. Ship types count by family, so AIS codes 70 to 79 are all
|
|
||||||
cargo; type 0 ("not available") and the reserved codes 100 to 255 count as
|
|
||||||
unknown. Altitude and speed stats cover airborne aircraft only, so parked
|
|
||||||
aircraft do not pull the means down. Every vessel answer, `find_vessel`
|
|
||||||
included, now reads AIS ship-type codes as their family and a destination
|
|
||||||
with no letters as unknown (quanticsoul4772, #1056).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- On Windows, `npm run doctor` and the Pinokio install no longer print Node's
|
|
||||||
DEP0190 warning ("Passing args to a child process with shell option true can
|
|
||||||
lead to security vulnerabilities"): npm.cmd's fixed subcommand is now part of
|
|
||||||
the shell command string instead of an argument array (quanticsoul4772, #1057).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Location search accepts two-dimensional WGS84 `geo:` URIs offline, with
|
|
||||||
the existing coordinate framing and label. Unsupported URI fields are passed
|
|
||||||
to the remaining search providers unchanged (cj-vana, #1071).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Update DOMPurify to 3.4.17, Sharp to 0.35.5 and source-map-js to 1.2.2 for
|
|
||||||
GHSA-p98j-92pf-mc4p, GHSA-6688-9rhm-gjv2, GHSA-wq5f-xc86-pv6w and
|
|
||||||
GHSA-68fv-2mgg-jv7q; `npm audit` now reports no vulnerabilities (NurstarK,
|
|
||||||
#1094).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Military aircraft keep their last heading and speed when an adsb.lol row omits
|
|
||||||
`track` or `gs`, instead of turning north and stopping; a grounded row without
|
|
||||||
a speed still reads 0 (Aditya, @Rohilalala, #1096).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Overpass data whose element tags contain a failure phrase ("timed out",
|
|
||||||
"runtime error", "too many requests" and similar, e.g. a place named "Timed
|
|
||||||
Out Escape Rooms") is returned as data instead of being refused and putting
|
|
||||||
the endpoint on cooldown; upstream failures are read from the remark (Aditya,
|
|
||||||
@Rohilalala, #1097).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Skip TLE sets whose line 1 and line 2 carry different catalog numbers, so a
|
|
||||||
spliced catalog cannot pair one satellite's elements with another's; the next
|
|
||||||
valid set still parses (VG31OP, #1101).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- A failed Esri Satellite or keyless terrain load still shows OSM or flat terrain
|
|
||||||
at once, but no longer lasts the whole session: while the fallback is on
|
|
||||||
screen, the real provider is retried in the background (30 seconds, doubling
|
|
||||||
to a 10-minute cap) and replaces it live once it loads. Map switches never
|
|
||||||
wait for a retry (Aditya, @Rohilalala, #1111).
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
- Exclude aircraft without a reported altitude from requested altitude bands;
|
|
||||||
unfiltered aircraft queries still retain them (rudycelekli, #1115).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Release superseded Google Street View open waits and suppress metadata
|
|
||||||
requests whose viewer was unmounted while the service loaded (rudycelekli,
|
|
||||||
#1117).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Label camera-tool Street View fallbacks as historical Google imagery rather
|
|
||||||
than the current camera view; keep unknown frame provenance explicit
|
|
||||||
(rudycelekli, #1118).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Scenes keep the "Scene not saved" status when a capture, update, append or
|
|
||||||
import cannot be saved, and an unreadable saved project no longer overwrites
|
|
||||||
the recovery checkpoint. `startScene()` resolves to its documented result,
|
|
||||||
`afterShotId` works without an explicit scene, and `scenesRun` counts the
|
|
||||||
distinct scenes queued (djabhi31, #1135).
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
- Fire-perimeter queries handle outlines and holes crossing the antimeridian:
|
|
||||||
areas enclosed near ±180° are included, areas at the opposite meridian are
|
|
||||||
excluded, and returned perimeter coordinates stay near the dateline. Incident
|
|
||||||
cards use the same circular longitude mean and compare multipart outline
|
|
||||||
areas along short longitude edges, keeping the card on the largest polygon
|
|
||||||
(Arianna Method, @ariannamethod, #1142).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Director shot actions land on the requested shot. A seek to a shot's start
|
|
||||||
could round a hair below it and select the end of the previous shot for some
|
|
||||||
shot counts and durations (Aditya, @Rohilalala, #1171).
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
- Keep missing CCTV numbers unknown instead of reading them as zero. `Number`
|
|
||||||
turns null, blank strings and booleans into 0 or 1, and the camera source
|
|
||||||
list sends an unknown number as JSON null, so a source-pack camera that left
|
|
||||||
out its pose reached the browser as zeros clamped to each field's floor —
|
|
||||||
facing north, pitch −2°, FOV 20°, range 120 m, mount 6 m and ground 0 m —
|
|
||||||
instead of the defaults (id-hash heading, −17°, 74°, 700 m, 24 m and the
|
|
||||||
city's ground); a null or blank position placed the camera at 0,0. The
|
|
||||||
server's `toFiniteNumber` and the browser's `safeNumber` now treat absent,
|
|
||||||
blank and boolean values as missing. The same conversion also stops a
|
|
||||||
Tallinn row with a null heading reading as a confident north-facing camera,
|
|
||||||
a Caltrans camera with a blank elevation losing its 150 m ground prior, and
|
|
||||||
a TfL or Catalonia camera with a null or blank coordinate being kept at 0,0
|
|
||||||
or on the zero meridian (omerbberk, #1172).
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
- Flights' ground floor works across the antimeridian. The viewer-distance
|
|
||||||
check behind the 150 km floor radius and its copy in the photoreal mesh-floor
|
|
||||||
sampler subtracted longitudes without wrapping, so two points 21 km apart
|
|
||||||
across 180° measured about 38,000 km and low or grounded aircraft just across
|
|
||||||
the line from the viewer were never floored. A contact's display corridor
|
|
||||||
walked a segment across 180° the long way round, spending its cell budget on
|
|
||||||
ground behind the contact, and a contact on the seam lost its cell hysteresis.
|
|
||||||
SEARCH NEARBY SITES on a view across 180° searched Google Places around
|
|
||||||
longitude 0, on the far side of the globe, with a radius sized from the view's
|
|
||||||
height alone; it now searches the view (Aditya, @Rohilalala, #1173).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Keep missing launch-pad coordinates unknown so launches use the location
|
|
||||||
fallback or stay off the map instead of appearing at 0°N 0°E. Missing stage
|
|
||||||
flight numbers no longer show "FLIGHT 0"
|
|
||||||
(Rudy Celekli, @rudycelekli, #1186).
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
- Add nine Spanish city bike systems to Bike Share and `get_bike_share`: BiciMAD
|
|
||||||
(Madrid), Valenbisi (Valencia), Bizi Zaragoza, Sevici (Seville), BIKI
|
|
||||||
(Valladolid), BiciCoruña, Dbizi (Donostia-San Sebastián), Bilbao Bizi and
|
|
||||||
BicinRivas, about 1,840 stations. Station names read underscores as word
|
|
||||||
breaks, and station capacity counts docks rather than PBSC's geofenced area
|
|
||||||
(ulihhhh, #1190).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Share Launch Library request cache keys across installations refreshing in the
|
|
||||||
same UTC minute, identify the app to the provider, and explain that standard
|
|
||||||
space-missions access needs no key. Optional higher-rate LL2 tokens are paid
|
|
||||||
upgrades; existing local cache and server-only token handling are unchanged
|
|
||||||
(rudycelekli, #1197).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Keep Space Missions marked STALE when Launch Library 2 falls back to cached
|
|
||||||
launch data, and return to NOMINAL after a fresh response without discarding
|
|
||||||
retained missions (rudycelekli, #1199).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Color CCTV cameras with a video feed violet. Cameras with an HLS, MP4 or
|
|
||||||
WebM feed were the same cyan as stills, so a DelDOT or Statens vegvesen
|
|
||||||
video camera could only be found by opening it. Stills stay cyan and the
|
|
||||||
active camera stays amber, and focus de-emphasis fades the violet the same
|
|
||||||
way (Danielslay86, #1200).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Draw CCTV stills at their source aspect on the monitor plane instead of
|
|
||||||
stretching them to the 16:9 canvas. Most DOT cameras publish 4:3 frames, so
|
|
||||||
every still was widened; they are now centered and pillarboxed on the dark
|
|
||||||
the placeholder already ends on (Danielslay86, #1201).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Report actual outcomes when voice enables or disables CCTV through the shared
|
|
||||||
layer lifecycle, including failed and cancelled changes (manjunath22466,
|
|
||||||
#1205).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Avoid starting or queuing a reply when the local channel rejects a typed OpenAI
|
|
||||||
command, while preserving an earlier accepted deferred input
|
|
||||||
(Manjunath, #1220).
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
- Add continuous globe rotation with direction and speed controls and a globe-fit
|
|
||||||
check before starting (manjunath22466, #1225).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Load Ontario 511 cameras again. 511on.ca's camera endpoint now requires a
|
|
||||||
free developer key; set `ONTARIO_511_API_KEY` to enable the pack, which is
|
|
||||||
otherwise skipped with one log line saying where to get a key
|
|
||||||
(quanticsoul4772, #1228).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Harden the Tarktee (Estonia) DATEX2 camera catalog fetches: refuse redirects,
|
|
||||||
cancel both response streams when either response fails or overflows the 4 MB
|
|
||||||
byte cap, and deduplicate camera IDs before per-pack prioritization
|
|
||||||
(yaka0007, #1233).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Serve the camera catalog while it refreshes. An expired catalog made every
|
|
||||||
camera request wait for the slowest pack to reload; it is now answered at
|
|
||||||
once while one background refresh runs, so only the first load waits. Estonia's
|
|
||||||
slow Tarktee feeds get 30 s instead of 15 s, so the pack no longer drops out
|
|
||||||
on about a third of loads (quanticsoul4772, #1234).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Require a geocoded containment anchor for ambiguous analyst region names,
|
|
||||||
preserve boundary provenance and cancellation safety, and retain exact-name
|
|
||||||
precedence for natural outlines (manjunath22466, #1235).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Add Taiwan's national freeway cameras from the Freeway Bureau, MOTC to CCTV:
|
|
||||||
~1,800 cameras read at runtime from the official MOTC 2.0 open-data table,
|
|
||||||
with on-demand snapshots and attribution. No API key is required; set
|
|
||||||
`CCTV_TAIWAN_ENABLED=0` to skip it (419vive, #1237).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Replace the blocky JPEG sky around the globe with the 9,096 stars of the
|
|
||||||
Yale Bright Star Catalogue, drawn as sharp points where they are at the
|
|
||||||
scene time, over a soft Milky Way from ESO/S. Brunier (CC BY 4.0). The Sun
|
|
||||||
and Moon stay; the anime style hides the Milky Way. Assets: 296 kB (gekh,
|
|
||||||
#1243).
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
- Prevent malformed Overpass filters from blocking the local server, and reject
|
|
||||||
cross-site or excessive query requests before reading their bodies
|
|
||||||
(asturksever, #1264; fixes #1248).
|
|
||||||
- Release unread responses from the optional Google-token probe so keyless
|
|
||||||
browser validation can settle; the Overpass gate recognizes only that probe's
|
|
||||||
expected 404 as an unavailable capability (asturksever, #1264).
|
|
||||||
- Make tracking arrival checks respect the aircraft update cadence while checking
|
|
||||||
orientation on the first reveal, avoiding false failures at faster render
|
|
||||||
rates (asturksever, #1264).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Honor Docker Compose trusted-peer and allowed-host settings from the shell
|
|
||||||
or project `.env`, and let `GEV_KEY_SETUP_TRUSTED_PEERS=none` disable gateway
|
|
||||||
access to Provider Settings before Vite starts (asturksever, #1265).
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
- Normalize station tags and retain valid mixed rows; skipped malformed rows still report degraded coverage (asturksever, #1268).
|
|
||||||
- Correct Radio QA push-to-talk setup to target the mounted voice controller (asturksever, #1268).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Coalesce refreshes and cool down opensky failures (asturksever, #1269).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Respect provider cooldowns and bound tle groups (asturksever, #1270).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Reject inherited style keys during restore (asturksever, #1271).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Retire unavailable history and keep local trails (asturksever, #1273).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Scan the kml root delimiter in bounded time (asturksever, #1274).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Preserve enable during first-load replacement (asturksever, #1275).
|
|
||||||
- Release disabled weather camera listeners and make teardown resource leaks fail the browser probe (asturksever, #1275).
|
|
||||||
- Keep visible-page weather teardown probes focused through the browser testing API (asturksever, #1275).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Bound each height request to one upstream chunk (asturksever, #1276).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Reserve singleflight before disk cache reads (asturksever, #1278).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Throttle mission hover picks with a trailing check for the last pointer position, and clear stale cursors (asturksever, #1279).
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
- Hold rendering only for animated content (asturksever, #1281).
|
|
||||||
- Exercise current vector road sources and published camera bounds in Traffic fixture QA (asturksever, #1281).
|
|
||||||
- Establish browser-level foreground visibility for Traffic QA and reject hidden-page samples (asturksever, #1281).
|
|
||||||
- Supply CORS headers on cross-origin traffic fixtures and report browser exceptions without disabling browser security (asturksever, #1281).
|
|
||||||
- Isolate the Traffic keyless browser scenario from the keyed page’s workers and intercepted cache (asturksever, #1281).
|
|
||||||
- Restore minimized test windows before screenshots so native compositor pauses cannot stall browser acceptance (asturksever, #1281).
|
|
||||||
- Intercept only Traffic provider URLs, leaving Cesium worker and module loading untouched (asturksever, #1281).
|
|
||||||
- Settle an unfinished Traffic height correction when the last animated point leaves, so the layer stops reporting loading once its render hold is released (asturksever, #1281).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Report cached CelesTrak satellite records as stale in the Data Layers row,
|
|
||||||
preserving separate core and dense catalog provenance through refresh,
|
|
||||||
removal and lifecycle cleanup (rudycelekli, #1283).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Keep aircraft route labels on the scheduled airport leg instead of accepting
|
|
||||||
its distant great-circle extensions, preserving generous endpoint tolerance
|
|
||||||
across the signed-angle branch cut (rudycelekli, #1284).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- The Street Level SINCE note sits on the value's line, in its type and a
|
|
||||||
size smaller: with KartaView lit it reads ANY DATE (by upload date). It
|
|
||||||
had lost its size variable in the Street Level slim-down and rendered
|
|
||||||
larger than the value, on a line of its own (asturksever, #1286).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Give ordinary sliders a shared native control and consistent track, thumb,
|
|
||||||
value, focus and disabled styling while preserving feature behavior and
|
|
||||||
specialized controls (mazy1998, #1287).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Add Simplified Chinese (zh-CN) to the language picker, with localized dates
|
|
||||||
and readable Chinese labels. Based on translations by @abiyeenzo (#914)
|
|
||||||
and @EDIT-Aeterna (#979).
|
|
||||||
(German, @gekh, #1298).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- Unify segmented selectors for model coverage, detection allocation, drawing
|
|
||||||
shape, Street View and imagery comparison while preserving their existing
|
|
||||||
behavior. Keep compact labels such as FILL and FLAT on one line, and let
|
|
||||||
collapsed Weather release space to the open rail panel. Reserve the full mode
|
|
||||||
button height in Recent Imagery so keyboard focus is not clipped (mazy1998, #1301).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Give ordinary Scene and CCTV actions shared native buttons with consistent
|
|
||||||
default, destructive and compact styling while preserving their existing
|
|
||||||
behavior and specialized controls (mazy1998, #1302).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Share native dropdown and numeric-field styling across Scene, CCTV, Display
|
|
||||||
and Radio controls, with a shared CCTV number-input builder that preserves
|
|
||||||
existing precision, validation and commit behavior (mazy1998, #1305).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Share read-only status badge presentation across CCTV source/frame labels,
|
|
||||||
calibration provenance and Weather coverage, preserving status meanings,
|
|
||||||
palettes, translations and announcements while preparing the view contract
|
|
||||||
for a future Vue migration (mazy1998, #1306).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Clarify shared field CSS ownership: CCTV owns calibration editor sizing,
|
|
||||||
while Radio and Cyber supply explicit field palettes without changing the
|
|
||||||
current appearance or native control behavior (mazy1998, #1309).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
- Validate provider response formats, strengthen camera source checks, and apply consistent API response protections (samehkhamis, #1317).
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
- Remove dead code across the repository: three modules nothing imports
|
|
||||||
(`src/ui.js`, `src/standalone/ui.js`, `src/data/localLayers.js`), exports
|
|
||||||
and locals nothing reads, a duplicate copy of the bundled dams data and an
|
|
||||||
unused ship model. The app no longer builds an unused default wind layer
|
|
||||||
and Bhote Koshi locator layer when it loads. Package exports are unchanged
|
|
||||||
(samehkhamis, #1321).
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
- Export the KartaView and Panoramax Street Level providers, so a host composing
|
|
||||||
its own provider list can offer the keyless providers without Mapillary
|
|
||||||
(samehkhamis, #1324).
|
|
||||||
- A Mapillary source can take a `graphEndpoint`, a Graph API base that adds the
|
|
||||||
token itself (such as a same-origin proxy). Its sequence lookups go there
|
|
||||||
through the source's transport, and the photo viewer uses MapillaryJS's
|
|
||||||
Graph provider at that endpoint, so no client token is needed in the page
|
|
||||||
(samehkhamis, #1324).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Add a phone layout for viewports 720px and narrower: the globe stays full
|
|
||||||
screen and a bottom tab bar (Layers, Cameras, Context, Search, More) opens one
|
|
||||||
panel sheet at a time, keeping attribution visible and scene STOP reachable.
|
|
||||||
Adapted from the phone tab-shell in #203 by blearymoth (samehkhamis, #1325).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Improve touch selection of aircraft and vessels through the shared contact
|
|
||||||
picker, reveal tracked aircraft details when opening Context, support downward
|
|
||||||
swipe dismissal of phone drawers while preserving scrolling and controls, and
|
|
||||||
enlarge the phone logo (samehkhamis, #1338).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Reject impossible KartaView photo timestamps, so 31 February is unknown
|
|
||||||
instead of rolling forward into March and passing the imagery date filter
|
|
||||||
(Rudy Celekli, @rudycelekli, #1341).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Keep an impossible wildfire acquisition date off the detection clock, so a
|
|
||||||
fire is not labeled with a later day it did not happen
|
|
||||||
(Rudy Celekli, @rudycelekli, #1342).
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- Add Traditional Chinese (zh-Hant) with language selection and localized
|
||||||
|
Chinese readouts: the Simplified Chinese catalog written in Traditional
|
||||||
|
characters, based on translations by @abiyeenzo (#914) and @EDIT-Aeterna
|
||||||
|
(#979) (German, @gekh, #1430).
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- The client's great-circle distances now come from one shared
|
|
||||||
`src/geoDistance.js` (`greatCircleKm`/`greatCircleMeters`, `{lat, lon}`
|
|
||||||
points). The Earth radius is standardized on 6371.0088 km, so distances shift
|
|
||||||
by about 1.4 ppm and region areas by about 2.8 ppm, and missing or
|
|
||||||
out-of-range points return `Infinity` (onionviolet, #287; Rajojha-1, #749).
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
- Add FL511 (Florida DOT) traffic cameras as an opt-in CCTV source pack:
|
|
||||||
the statewide ArcGIS catalog, read a page at a time under a byte cap with
|
|
||||||
redirects refused, and frames pinned to FL511's two image hosts over HTTPS.
|
|
||||||
FL511's terms limit its content to individual, personal use, and any other
|
|
||||||
use (a public deployment included) needs FDOT's express written consent, so
|
|
||||||
the pack stays off unless `CCTV_FLORIDA_ENABLED=1`. Headings come from the
|
|
||||||
catalog's direction codes, and camera ids come from the image channel, since
|
|
||||||
the catalog's ID column repeats across regions. `CCTV_FLORIDA_MAX_SOURCES`
|
|
||||||
sets the cap (Danielslay86, #605).
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
- Bound what a caller can grow through `/api/terrain/heights`. Every distinct
|
|
||||||
5-decimal point was a permanent in-memory entry, a permanent line in an
|
|
||||||
uncapped `.gev-cache/terrain-heights.json`, and a live upstream call, with
|
|
||||||
nothing limiting the rate. The point cache now holds at most 50,000 entries
|
|
||||||
and evicts oldest-first, a cache file above
|
|
||||||
24 MB is not read back, and the route is limited to 90 requests/minute per
|
|
||||||
client (270 global). Evicting a point costs one refetch, never a wrong
|
|
||||||
answer, since terrain does not move. Ordinary use is unaffected: a full
|
|
||||||
`test:track` run drives 10 requests, peaking at 8/min (daikaginza, #686).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- The dev server no longer watches `.gev-cache` and `.gev-logs`. Providers write
|
|
||||||
cache files and logs there while the app runs, and none of it is source, so
|
|
||||||
the watcher now skips it; this guards against the DNS-lookup delays heavy
|
|
||||||
cache writes have caused, though no difference was measured on current main
|
|
||||||
(monwiss, #761).
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
- Read satellite catalog numbers written in Alpha-5, the TLE spelling of
|
|
||||||
numbers above 99999: a letter for the first two digits, skipping I and O
|
|
||||||
(`A0000` is 100000, `Z9999` is 339999). The satellites layer converted such a
|
|
||||||
field to `NaN`, so every Alpha-5 satellite shared one catalog entry and all
|
|
||||||
but the first were dropped as duplicates; the satellite tools read the same
|
|
||||||
field as no number, so such a satellite could not be found by it. The layer
|
|
||||||
and the tools now decode the field in `src/sources/tle.js`, and reject a
|
|
||||||
malformed one (a sign, decimal point, hex, embedded space or stray letter)
|
|
||||||
instead of reading part of it as a number. This is Alpha-5 TLE
|
|
||||||
compatibility: CelesTrak's TLE feed carries no satellite above 99999, which
|
|
||||||
needs the OMM feed (#1191) (Honorius Neogy, #767, fixes #751).
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
- Changelog entries are now one file per pull request. A change adds
|
|
||||||
`changelog.d/<pull request>-<slug>.md` instead of editing the top of
|
|
||||||
`CHANGELOG.md`, which two pull requests in flight could not share — it was the
|
|
||||||
most frequent merge conflict in the open queue, and for six of them the only
|
|
||||||
one. `npm run changelog` splices the directory into `CHANGELOG.md` at release
|
|
||||||
time and empties it; `npm run changelog:check` reports the batch and changes
|
|
||||||
nothing. Fragment text is copied through unchanged (daikaginza, #787).
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
- Close the compact Radio disclosure when an adaptive tactical right-rail
|
|
||||||
restore directly expands Context, before Space Missions can be covered.
|
|
||||||
A repeated Context-open request also clears stale disclosure state.
|
|
||||||
Real PanelChrome constructor wiring and the rail reflow path are covered
|
|
||||||
by focused regressions. Credits: kvnloo (#816), raysung-2026's original
|
|
||||||
#804 report, asturksever's review, and quanticsoul4772's independent
|
|
||||||
Windows diagnosis.
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Add optional Gemini Live voice to the standalone app, with a provider chooser,
|
|
||||||
server-held API key, microphone streaming, shared map actions and captions.
|
|
||||||
Switching providers ends the old conversation and preserves the input mode
|
|
||||||
(Manjunath, #872).
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
- Extract a reusable model-free World Packet from the existing voice
|
|
||||||
`get_current_view_state` owner without restoring disabled layers to the
|
|
||||||
model context. Keep the enabled-only payload, disabled-layer count, source
|
|
||||||
provenance, action result and view-state contract; read the layer roster once
|
|
||||||
per packet; voice retains the speech/card envelope at its own boundary.
|
|
||||||
Credits: original GEV voice/layer contributors and #880 implementation.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
- Give each Statens vegvesen camera its own ground height from Kartverket's
|
|
||||||
keyless point-height API (CC BY 4.0) instead of one flat 150 m prior, so
|
|
||||||
mountain-pass cameras no longer sit hundreds of metres below the terrain
|
|
||||||
where neither the point-height prior nor the ground snap resolves. About 850
|
|
||||||
of ~855 cameras get a height; the rest keep the old prior. Requests carry 20
|
|
||||||
points, the most the API answers from its 1 m terrain model, eight at a
|
|
||||||
time; a cold lookup takes about 6–13 s. Cameras over water use the water
|
|
||||||
surface rather than the sea-floor depth the API returns there; for bridge
|
|
||||||
cameras that is still below the deck. Heights are cached for good and misses
|
|
||||||
for 6 h, so a refresh only asks about new or moved cameras. After a
|
|
||||||
failing request (a 408, 429 or 5xx, a network error, a request over 10 s or
|
|
||||||
an unreadable body) the lookup stops and backs off (Retry-After, else
|
|
||||||
30 min, longer than the refresh). The lane waits at most 12 s from its
|
|
||||||
start, and only on a cold cache; a slower first lookup leaves the cameras it
|
|
||||||
had not reached on the flat prior until the next 15-minute refresh, and
|
|
||||||
keeps filling the cache meanwhile.
|
|
||||||
`CCTV_VEGVESEN_HEIGHTS=0` turns it off. Follow-up to #929 (trygvehhh, #945).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- Recent Imagery: opening DETAILS scrolls its card into view when it opens below
|
|
||||||
the fold. The panel used to restore the previous scroll position right after
|
|
||||||
the reveal, so the card stayed out of sight (Ian Blenke, #970).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- Cross-site browser requests can no longer spend the TomTom traffic-tile
|
|
||||||
budget through `/api/tomtom/*`. The shared same-site gate runs before status,
|
|
||||||
cache, and upstream work; same-origin app requests and non-browser local
|
|
||||||
callers remain supported (Sunil56224972, #973).
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
- Street Level is rebuilt around one small provider registry: Mapillary,
|
|
||||||
KartaView, Panoramax and Google Street View share one coverage drawer and
|
|
||||||
one photo viewer, one source lit at a time. Floating and docked panels,
|
|
||||||
FOLLOW, surface modes, the legend and the Mapillary proxy's disk cache are
|
|
||||||
gone; `.gev-cache/mapillary/` from earlier runs is no longer read and can be
|
|
||||||
deleted (gekh, #974).
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
- Cross-site pages can no longer fill the terrain-height cache or spend the
|
|
||||||
visitor's own `/api/terrain/heights` rate-limit quota through the visitor's
|
|
||||||
browser. The route refuses a cross-site `Sec-Fetch-Site` or a foreign or
|
|
||||||
opaque `Origin` before rate limiting, cache access or upstream work. Unlike
|
|
||||||
the cost-bearing routes it still answers same-origin requests that arrive
|
|
||||||
through a reverse proxy, so terrain keeps rendering behind nginx, Caddy or
|
|
||||||
Cloudflare instead of falling back to the geoid (Sunil56224972, daikaginza,
|
|
||||||
#975).
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
- Add Hong Kong Transport Department traffic snapshot cameras as a keyless CCTV
|
|
||||||
source pack: the public DATA.GOV.HK English locations XML, frames pinned to
|
|
||||||
`tdcctv.data.one.gov.hk`, with DATA.GOV.HK Terms of Use attribution. By
|
|
||||||
default nearly the full TD catalog (~1,013 cameras; cap 1,100) loads,
|
|
||||||
prioritized across Hong Kong Island, Kowloon and the New Territories. Headings
|
|
||||||
use the shared id-hash fallback at low confidence. `CCTV_HK_MAX_SOURCES` sets
|
|
||||||
the cap (8–1,200) and `CCTV_HK_ENABLED=0` turns the pack off (JCY2515, #990).
|
|
||||||
+4
-1
@@ -22,5 +22,8 @@ services:
|
|||||||
# UI land in the .env on your host instead of vanishing with the
|
# UI land in the .env on your host instead of vanishing with the
|
||||||
# container. node_modules stays in the image: sharp and friends are
|
# container. node_modules stays in the image: sharp and friends are
|
||||||
# compiled for Linux and must not be shadowed by a host install.
|
# compiled for Linux and must not be shadowed by a host install.
|
||||||
- .:/app
|
# `:z` relabels the checkout for containers on SELinux hosts (Fedora,
|
||||||
|
# RHEL); without it the container cannot read /app at all and Node
|
||||||
|
# reports the launcher as "Cannot find module". Elsewhere it is ignored.
|
||||||
|
- .:/app:z
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
@@ -81,8 +81,6 @@ moves it. Do not label all ingestion modules platform-independent by filename.
|
|||||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `src/data/manager.js` | Lifecycle/panel compatibility and existing tests; normal assembly constructs lifecycle and presentation separately |
|
| `src/data/manager.js` | Lifecycle/panel compatibility and existing tests; normal assembly constructs lifecycle and presentation separately |
|
||||||
| `src/data/<layer>.js` | Legacy default instances; new assembly calls layer factories with supplied sources |
|
| `src/data/<layer>.js` | Legacy default instances; new assembly calls layer factories with supplied sources |
|
||||||
| `src/data/localGeojson.js` | Infrastructure compatibility factories; catalog imports services from `src/app/localGeojsonServices.js` directly |
|
|
||||||
| `src/app/sourceSlot.js` | Re-export for direct callers/tests; portable slot ownership is `src/sources/sourceSlot.js` |
|
|
||||||
| `src/app/sources.js`, `src/services/application.js` | Existing default/override API; current application construction supplies its own instances |
|
| `src/app/sources.js`, `src/services/application.js` | Existing default/override API; current application construction supplies its own instances |
|
||||||
| `server/providers/local.js` | Existing Node composition barrel and provider helper re-exports |
|
| `server/providers/local.js` | Existing Node composition barrel and provider helper re-exports |
|
||||||
|
|
||||||
@@ -600,6 +598,10 @@ state subscriptions retain their existing event contract.
|
|||||||
tracking handoff. It reads Cockpit admission and uses supplied tracking operations.
|
tracking handoff. It reads Cockpit admission and uses supplied tracking operations.
|
||||||
- `ui/share-restoration` owns the initial restore transaction, layer coordinator,
|
- `ui/share-restoration` owns the initial restore transaction, layer coordinator,
|
||||||
status notices and gesture/timer cleanup.
|
status notices and gesture/timer cleanup.
|
||||||
|
- `share/view-codec` is the share-link codec (`decodeShareView`,
|
||||||
|
`encodeShareView`). It is pure (no DOM, no Cesium), so a host's server can
|
||||||
|
read a `/share/<code>` link, for example to describe it in link previews.
|
||||||
|
`share/link-path` reads and builds the `/share/<code>` address itself.
|
||||||
- `ui/visual-settings` owns style preferences, detection overrides, display inputs
|
- `ui/visual-settings` owns style preferences, detection overrides, display inputs
|
||||||
and IR cleanup. Its engine services and panel operations are explicit.
|
and IR cleanup. Its engine services and panel operations are explicit.
|
||||||
- `ui/panel-chrome` owns disclosure, docking, collapse preferences and temporary
|
- `ui/panel-chrome` owns disclosure, docking, collapse preferences and temporary
|
||||||
|
|||||||
+269
-34
@@ -1,5 +1,31 @@
|
|||||||
# God's Eye View Current State
|
# God's Eye View Current State
|
||||||
|
|
||||||
|
Docker Compose mounts the checkout with the shared SELinux `:z` label so
|
||||||
|
containers can read it on enforcing SELinux hosts. The separate image-owned
|
||||||
|
`node_modules` volume and loopback-only port publishing remain unchanged.
|
||||||
|
|
||||||
|
The HUD's LAT/LON degrees-minutes-seconds readout rounds the coordinate once, to
|
||||||
|
hundredths of a second, and then splits it. A value within 0.005" of the next
|
||||||
|
whole minute carries into the minutes and degrees, so `-122.35` reads
|
||||||
|
`122°21'00.00"W` rather than `122°20'60.00"W`. The field widths and hemisphere
|
||||||
|
letters are unchanged.
|
||||||
|
|
||||||
|
Aircraft detection candidates reuse normalized display text until callsign or
|
||||||
|
registration changes, including in-place enrichment. Each candidate resolves its
|
||||||
|
fleet model once per collection. Positions, model ownership and tracked-aircraft
|
||||||
|
anchors remain live on every collection; contact admission is unchanged.
|
||||||
|
|
||||||
|
Civil aircraft share one delayed render timestamp per fleet update. Extrapolated
|
||||||
|
fixes reuse their local coordinate frame until their coordinates change, including
|
||||||
|
in-place ground-height corrections. Update cadence, contact counts, interpolation,
|
||||||
|
turn rates and tracked-aircraft motion are unchanged. Cached frames are weakly
|
||||||
|
keyed by each position fix and do not retain retired history.
|
||||||
|
|
||||||
|
Touch-first devices use three times the default Cesium zoom sensitivity, so
|
||||||
|
pinching traverses globe and street scales with fewer gestures. Cesium retains
|
||||||
|
its distance-aware zoom, collision handling and minimum/maximum distance limits.
|
||||||
|
Fine-pointer desktop mouse and trackpad sensitivity is unchanged.
|
||||||
|
|
||||||
Analyst region queries resolve unique Natural Earth names offline without
|
Analyst region queries resolve unique Natural Earth names offline without
|
||||||
geocoding, so Gold Coast and Ivory Coast name the West African natural regions.
|
geocoding, so Gold Coast and Ivory Coast name the West African natural regions.
|
||||||
Duplicate names require a geocoded containment anchor; when several entries for
|
Duplicate names require a geocoded containment anchor; when several entries for
|
||||||
@@ -9,13 +35,7 @@ broader suffix variants. Follow-ups retain boundary provenance, and cancelled
|
|||||||
lookups preserve the previous analyst cohort and boundary source. Navigation
|
lookups preserve the previous analyst cohort and boundary source. Navigation
|
||||||
search keeps a successful geocode, with no search or view-based region recovery.
|
search keeps a successful geocode, with no search or view-based region recovery.
|
||||||
|
|
||||||
Overpass reserves each normalized query before its disk-cache lookup. The
|
Manager-owned periodic network updates pause while the document is hidden. Returning to a visible tab runs one overdue update per enabled layer without overlapping an active refresh and restarts that layer's polling schedule from the return, so the next poll follows one interval later whether or not the catch-up update succeeds; explicit refresh requests remain available. Transit also defers camera-driven feed admission while hidden and rechecks coverage on return. Visibility listeners are released with their polling intervals.
|
||||||
initial disk lookup, upstream quota admission, fetching and memory publication
|
|
||||||
are coalesced under that reservation. A caller that joined a reservation
|
|
||||||
rejected by another client's quota retries admission with its own quota.
|
|
||||||
Stale fallback runs per response after the reservation is released, and disk
|
|
||||||
writes finish asynchronously. Settled and rejected reservations are removed
|
|
||||||
only while they still own their map entry.
|
|
||||||
|
|
||||||
Terrain height requests accept at most 64 points, matching the browser service's
|
Terrain height requests accept at most 64 points, matching the browser service's
|
||||||
existing batch size and one bounded upstream chunk. Larger requests return HTTP
|
existing batch size and one bounded upstream chunk. Larger requests return HTTP
|
||||||
@@ -176,6 +196,23 @@ Regression coverage lives in `src/tools/area.test.mjs`,
|
|||||||
`src/tools/queries/environment.test.mjs` and
|
`src/tools/queries/environment.test.mjs` and
|
||||||
`src/layers/perimeters/cards.test.mjs`.
|
`src/layers/perimeters/cards.test.mjs`.
|
||||||
|
|
||||||
|
## Scope display controls
|
||||||
|
|
||||||
|
Display's native Scope checkbox defaults ON with feather at 100%, adjustable
|
||||||
|
from 0–200%. OFF disables the slider while retaining its value. HUD layout
|
||||||
|
changes preserve both settings; share links and scenes restore them. The
|
||||||
|
historical missing-`scf` fallback remains 35%; explicit 0% keeps the hard edge.
|
||||||
|
|
||||||
|
With Scope OFF, Tactical, Operator and Minimal add text-sized black backplates
|
||||||
|
at 70% opacity behind telemetry, vertical metadata and scene chrome. Original
|
||||||
|
HUD text color, glow and opacity are preserved. Scope OFF brightens the tagline
|
||||||
|
and Active Style label with the shared secondary text color. The title plate
|
||||||
|
covers the eye and title without moving the title or tagline. Scope ON retains
|
||||||
|
the original HUD presentation; Cyber retains its existing skin.
|
||||||
|
|
||||||
|
`node scripts/qa-scope-hud.mjs` checks native input, range, layout persistence,
|
||||||
|
restoration and both Scope presentations. `QA_SCREENSHOT_DIR` saves screenshots.
|
||||||
|
|
||||||
## Cyber HUD — September 30, 2026
|
## Cyber HUD — September 30, 2026
|
||||||
|
|
||||||
Display > HUD > Layout includes Cyber, also available through the HUD voice
|
Display > HUD > Layout includes Cyber, also available through the HUD voice
|
||||||
@@ -286,6 +323,10 @@ catalog construction owns each instance.
|
|||||||
Wind animates one forecast without advancing forecast time. Separate Weather
|
Wind animates one forecast without advancing forecast time. Separate Weather
|
||||||
observation layers provide radar and satellite history; none claims measured cloud volume.
|
observation layers provide radar and satellite history; none claims measured cloud volume.
|
||||||
Weather imagery releases its camera move listener on disable and registers it once on re-enable. Disabling also clears active imagery work.
|
Weather imagery releases its camera move listener on disable and registers it once on re-enable. Disabling also clears active imagery work.
|
||||||
|
A staged weather frame with loaded tiles and no pending requests rechecks
|
||||||
|
readiness on a single 150 ms wake timer instead of requesting every render.
|
||||||
|
The existing readiness conditions and two settled frames still govern promotion;
|
||||||
|
completion, cancellation, replacement and teardown clear the wake timer.
|
||||||
Weather imagery drapes onto the globe basemap. On photorealistic 3D Tiles each
|
Weather imagery drapes onto the globe basemap. On photorealistic 3D Tiles each
|
||||||
observed product is instead a raised, translucent shell: one rectangle over the
|
observed product is instead a raised, translucent shell: one rectangle over the
|
||||||
product bounds at a fixed height (global infrared 5.5 km, regional infrared
|
product bounds at a fixed height (global infrared 5.5 km, regional infrared
|
||||||
@@ -411,6 +452,9 @@ Cyclones combine fixed NHC/CPHC status and summary GIS endpoints through a bound
|
|||||||
same-origin provider. The status endpoint lacks browser CORS; each installation
|
same-origin provider. The status endpoint lacks browser CORS; each installation
|
||||||
fetches keyless public sources at runtime, with no centralized ingestion service.
|
fetches keyless public sources at runtime, with no centralized ingestion service.
|
||||||
The five-minute snapshot covers Atlantic and eastern/central North Pacific only.
|
The five-minute snapshot covers Atlantic and eastern/central North Pacific only.
|
||||||
|
Classification labels follow NHC status codes: `PC` is a potential tropical
|
||||||
|
cyclone, `PTC` a post-tropical cyclone, and `STS`/`STD` a subtropical
|
||||||
|
storm/depression. Unknown codes remain visible unchanged.
|
||||||
Current positions, advisory issue time and position time stay distinct. NHC may
|
Current positions, advisory issue time and position time stay distinct. NHC may
|
||||||
release an advisory before its nominal time (routinely up to about 30 minutes,
|
release an advisory before its nominal time (routinely up to about 30 minutes,
|
||||||
rarely about an hour), so advisory issue and position times up to and including
|
rarely about an hour), so advisory issue and position times up to and including
|
||||||
@@ -423,6 +467,8 @@ than relabeling older geometry. The cone represents forecast center uncertainty,
|
|||||||
not storm size or the full hazard area. Successful empty and unavailable states
|
not storm size or the full hazard area. Successful empty and unavailable states
|
||||||
are distinct. One owned native Cesium data source preserves polygon holes and
|
are distinct. One owned native Cesium data source preserves polygon holes and
|
||||||
geographic seams and releases on disable; forecast animation is not implied.
|
geographic seams and releases on disable; forecast animation is not implied.
|
||||||
|
NHC movement speed is converted from miles per hour to rounded knots by the
|
||||||
|
proxy; advisory wind intensity is already in knots and is passed through.
|
||||||
Cyclone markers, labels, tracks and cones are hidden beyond the horizon on every map source.
|
Cyclone markers, labels, tracks and cones are hidden beyond the horizon on every map source.
|
||||||
Consult the linked official advisory for safety decisions.
|
Consult the linked official advisory for safety decisions.
|
||||||
|
|
||||||
@@ -459,6 +505,14 @@ than a reading. Heading keeps its existing separate check for its own sentinel.
|
|||||||
Genuine readings are unchanged, including a stopped vessel's zero and the
|
Genuine readings are unchanged, including a stopped vessel's zero and the
|
||||||
highest speed and course the fields can encode.
|
highest speed and course the fields can encode.
|
||||||
|
|
||||||
|
For an already tracked vessel, AISStream static messages update name, type,
|
||||||
|
destination and IMO without changing its position, motion, fix timestamps,
|
||||||
|
position freshness or trail. Non-empty static values replace earlier values;
|
||||||
|
omitted, empty or unavailable fields retain known metadata across Class B's
|
||||||
|
separate reports and in the cache used by the next position update. Position
|
||||||
|
reports still prefer their own metadata name. Invalid Class B parts are ignored;
|
||||||
|
absent fields prefer the latest live value over the static cache.
|
||||||
|
|
||||||
Native `<select>` controls declare a dark colour scheme and explicit option
|
Native `<select>` controls declare a dark colour scheme and explicit option
|
||||||
colours, so the browser-painted option list matches the panel it drops out of.
|
colours, so the browser-painted option list matches the panel it drops out of.
|
||||||
The scheme is declared on the controls rather than on the document root, so
|
The scheme is declared on the controls rather than on the document root, so
|
||||||
@@ -517,6 +571,36 @@ replacement session; audio meters release failed initialization and reject revok
|
|||||||
frames. Delayed action results and post-capture continuations cannot resume a
|
frames. Delayed action results and post-capture continuations cannot resume a
|
||||||
stopped conversation or send output into a replacement. See [voice ownership](VOICE-OWNERSHIP.md).
|
stopped conversation or send output into a replacement. See [voice ownership](VOICE-OWNERSHIP.md).
|
||||||
|
|
||||||
|
The opt-in voice panel shares Preview and Voice Settings, with browser-local
|
||||||
|
Preview and inactivity preferences. Voice-requested timeout changes apply directly
|
||||||
|
without a confirmation question; results distinguish a live change from a saved
|
||||||
|
browser preference. `set_voice_inactivity_timeout` accepts integer minutes from 1–60,
|
||||||
|
or 0 for None. Named `voice-settings` and `voice-preview` panel targets exit
|
||||||
|
Cockpit before opening the requested voice UI. `control_cockpit` also accepts
|
||||||
|
`voicePanel: "preview" | "settings"` for an explicit exit-and-show request.
|
||||||
|
Newer manual choices, Stop and provider replacement prevent an older operation
|
||||||
|
from revealing its UI. Ambiguous requests to open settings ask whether the user
|
||||||
|
means Voice Settings or Provider Settings before opening either panel. Named
|
||||||
|
Voice Settings applies directly; Provider Settings opens the existing local
|
||||||
|
provider dialog when available. Visual Presets targets the visual controls tray,
|
||||||
|
while Scenes targets saved scenes. Selecting Preview does not enable a disabled
|
||||||
|
Preview preference. Unverified speech captions use the localized Recent
|
||||||
|
speech label; ordinary user captions retain YOU. Caption attribution does not
|
||||||
|
control tool dispatch.
|
||||||
|
|
||||||
|
Voice inactivity waits for local input, output and actions to become quiet.
|
||||||
|
A matching OpenAI response that explicitly completes without audio can restore
|
||||||
|
idle eligibility; completion of an audio response does not prove playback has
|
||||||
|
drained. Missing or ambiguous output evidence remains conservative. Gemini
|
||||||
|
retires its per-audio observer wrappers on close, replacement or session end,
|
||||||
|
so repeated Stop/Start does not retain old audio instances until the panel closes.
|
||||||
|
The panel keeps the model tier and regular cost estimate in Voice Settings;
|
||||||
|
warning, capped and unknown cost states also appear in the dock heading when
|
||||||
|
Settings is closed. The existing hard cost cap still stops the session and
|
||||||
|
announces the reason.
|
||||||
|
|
||||||
|
Automatic Voice Preview uses output from the active mounted provider, including late replies that may answer an older request. Manual panel choices remain until genuine local input rearms automatic presentation. Preview stays off when disabled, remains hidden in Cockpit, and retires on Stop or provider replacement. Display events do not grant command authority.
|
||||||
|
|
||||||
Contacts nearest-aircraft follow-ups retain the snapshot's distance basis even
|
Contacts nearest-aircraft follow-ups retain the snapshot's distance basis even
|
||||||
after the camera moves. An explicit new scope replaces that basis. Provider
|
after the camera moves. An explicit new scope replaces that basis. Provider
|
||||||
instructions require an analyst query for requested lists and use the requested
|
instructions require an analyst query for requested lists and use the requested
|
||||||
@@ -531,6 +615,9 @@ to layer toggles, fly_to_location, frame_overhead, select_nearest_aircraft,
|
|||||||
get_entity_context, annotate_map and analyst_query. Analyst speech and the card
|
get_entity_context, annotate_map and analyst_query. Analyst speech and the card
|
||||||
share one deterministic count/scope headline; lower bounds use "At least" and
|
share one deterministic count/scope headline; lower bounds use "At least" and
|
||||||
partial answers name the unanswered layers. `get_current_view_state` lists enabled layers only.
|
partial answers name the unanswered layers. `get_current_view_state` lists enabled layers only.
|
||||||
|
FIRMS and ALPR entity labels remain English in shared context and tool data;
|
||||||
|
voice-card titles and pointer presentation translate those known labels at display
|
||||||
|
time, while arbitrary names and callsigns remain unchanged.
|
||||||
The model-free `gods-eye-view/agent/world-packet` export exposes
|
The model-free `gods-eye-view/agent/world-packet` export exposes
|
||||||
`getWorldPacket({ viewer, styleManager, dataManager, sceneDirector })` to
|
`getWorldPacket({ viewer, styleManager, dataManager, sceneDirector })` to
|
||||||
non-voice consumers. It reads the same enabled-only state and feed
|
non-voice consumers. It reads the same enabled-only state and feed
|
||||||
@@ -750,6 +837,12 @@ transports and cancellation through response parsing. Stop and application
|
|||||||
teardown abort pending connections; reconnect requests a new client secret.
|
teardown abort pending connections; reconnect requests a new client secret.
|
||||||
Microphone, radio handoff, tool schemas and default model behavior are preserved.
|
Microphone, radio handoff, tool schemas and default model behavior are preserved.
|
||||||
|
|
||||||
|
Coordinate search accepts whitespace-separated decimal degrees with prefix or
|
||||||
|
suffix hemisphere letters, including `40.7128 N 74.0060 W`. Components require
|
||||||
|
an actual separator and an unambiguous hemisphere assignment; malformed numeric
|
||||||
|
fragments and scientific notation fall through to geocoders instead of becoming
|
||||||
|
exact coordinate results.
|
||||||
|
|
||||||
Geospatial lookups are composed through `src/search`: forward/reverse geocoding,
|
Geospatial lookups are composed through `src/search`: forward/reverse geocoding,
|
||||||
text/nearby search and routing use configurable providers/endpoints. Annotation,
|
text/nearby search and routing use configurable providers/endpoints. Annotation,
|
||||||
HUD and voice consumers share the configured service. Existing Google/Photon
|
HUD and voice consumers share the configured service. Existing Google/Photon
|
||||||
@@ -1051,6 +1144,10 @@ Shot selection updates the highlight without replacing the row, preserving
|
|||||||
native double-click rename. Replacing rows revokes their old listeners. Disposal stops controls immediately;
|
native double-click rename. Replacing rows revokes their old listeners. Disposal stops controls immediately;
|
||||||
late file and failed-action completions cannot update removed presentation.
|
late file and failed-action completions cannot update removed presentation.
|
||||||
|
|
||||||
|
Director clock listeners, interaction observers and scene release failures no
|
||||||
|
longer leave the director inconsistent, and a phase error is kept when cleanup
|
||||||
|
also fails.
|
||||||
|
|
||||||
## Cockpit component ownership
|
## Cockpit component ownership
|
||||||
|
|
||||||
Cockpit presentation is separated from its camera/controller behavior. Existing
|
Cockpit presentation is separated from its camera/controller behavior. Existing
|
||||||
@@ -1269,6 +1366,8 @@ Stopping the controller revokes animation before asynchronous UI teardown; final
|
|||||||
destruction removes its stages and restores the borrowed bloom configuration.
|
destruction removes its stages and restores the borrowed bloom configuration.
|
||||||
Snow's sideways sway stays within a fixed range at any session age; Wind scales
|
Snow's sideways sway stays within a fixed range at any session age; Wind scales
|
||||||
its amplitude.
|
its amplitude.
|
||||||
|
Sharpen runs only when enabled with intensity above 0.001. At slider zero its
|
||||||
|
stage is disabled; raising the slider resumes it when sharpen is enabled.
|
||||||
|
|
||||||
## Display control ownership
|
## Display control ownership
|
||||||
|
|
||||||
@@ -1499,7 +1598,11 @@ camera is refused there with a JSON 404 and is served solely through
|
|||||||
## GBFS upstream bounds
|
## GBFS upstream bounds
|
||||||
|
|
||||||
Station information is normalized in `src/sources/gbfsStations.js` for both the
|
Station information is normalized in `src/sources/gbfsStations.js` for both the
|
||||||
globe layer and `get_bike_share`. Names accept legacy strings or GBFS v3 Localized
|
globe layer and `get_bike_share`. Stations with missing, blank, nonnumeric or
|
||||||
|
out-of-range coordinates (latitude outside ±90, longitude outside ±180) are
|
||||||
|
skipped. Numbers and nonblank numeric strings are accepted, including genuine
|
||||||
|
zero and the `latitude`/`longitude` aliases; other JSON types are rejected.
|
||||||
|
Names accept legacy strings or GBFS v3 Localized
|
||||||
String arrays regardless of the reported feed version. Nonempty English text
|
String arrays regardless of the reported feed version. Nonempty English text
|
||||||
(including regional `en-*` tags, case-insensitive) takes precedence, then the first
|
(including regional `en-*` tags, case-insensitive) takes precedence, then the first
|
||||||
usable translation. An unusable `name` falls back to `short_name` with the same
|
usable translation. An unusable `name` falls back to `short_name` with the same
|
||||||
@@ -1669,6 +1772,14 @@ The TomTom `/api/tomtom/*` routes run the shared same-site admission gate before
|
|||||||
status, cache or upstream work, refusing cross-site browser requests while
|
status, cache or upstream work, refusing cross-site browser requests while
|
||||||
preserving same-origin app requests and non-browser local callers. The browser
|
preserving same-origin app requests and non-browser local callers. The browser
|
||||||
layers and their rendering remain in their existing modules.
|
layers and their rendering remain in their existing modules.
|
||||||
|
Past the daily tile budget, or while TomTom fails, the proxy answers with its
|
||||||
|
last copy of a tile (`x-tomtom-cache: STALE-BUDGET` / `STALE-ERROR`) however old
|
||||||
|
it is, and every tile carries `x-tomtom-fetched-at`, the proxy fetch time
|
||||||
|
(or the persisted cache file time after restart). The flow source keeps that mark with each decoded tile, so
|
||||||
|
`get_traffic_flow` returns `stale` and `oldest_tile_at` and says when traffic
|
||||||
|
may be out of date instead of presenting cached tiles as live. Pending freshness
|
||||||
|
metadata is released when body reads fail, are cancelled, or exceed their size
|
||||||
|
limit, as well as when a tile decodes or the source resets.
|
||||||
GBFS null or empty availability counts retain an unknown value through station
|
GBFS null or empty availability counts retain an unknown value through station
|
||||||
parsing and rendering; they use the neutral station color rather than reporting
|
parsing and rendering; they use the neutral station color rather than reporting
|
||||||
an empty dock. Explicit zero and caller-supplied numeric fallbacks remain valid.
|
an empty dock. Explicit zero and caller-supplied numeric fallbacks remain valid.
|
||||||
@@ -1793,6 +1904,12 @@ supported. `.env.example` and `pinokio/_ENVIRONMENT` document both entries.
|
|||||||
The Street View headings tool uses the same server-first selection after
|
The Street View headings tool uses the same server-first selection after
|
||||||
resolving environment overrides per variable; its explicit `--key` wins.
|
resolving environment overrides per variable; its explicit `--key` wins.
|
||||||
|
|
||||||
|
In standalone development and preview servers, a missing Google tile-token
|
||||||
|
provider returns an uncached `204 No Content` for `GET /api/google/tiles-token`
|
||||||
|
(and HEAD) instead of a browser-visible 404. The fallback is registered after
|
||||||
|
real token providers and before the unknown-API handler, so existing Google
|
||||||
|
browser keys, provider-issued tokens, and unrelated API routes remain unchanged.
|
||||||
|
|
||||||
## Infrastructure marker visibility
|
## Infrastructure marker visibility
|
||||||
|
|
||||||
Datacenter/dam registration uses fresh reusable factories with the application's
|
Datacenter/dam registration uses fresh reusable factories with the application's
|
||||||
@@ -1916,6 +2033,10 @@ Updated: September 23, 2026
|
|||||||
|
|
||||||
## Aircraft and vessel server modules
|
## Aircraft and vessel server modules
|
||||||
|
|
||||||
|
OpenSky credential-file failures emit a fixed warning once per process; neither
|
||||||
|
JSON parser excerpts nor filesystem error messages are logged. The environment
|
||||||
|
example uses an absolute path compatible with the macOS launchers.
|
||||||
|
|
||||||
Standalone aircraft routes now live in `server/providers/aircraft/`: OpenSky
|
Standalone aircraft routes now live in `server/providers/aircraft/`: OpenSky
|
||||||
state vectors and regional fallback, adsb.lol military positions, ADSBDB
|
state vectors and regional fallback, adsb.lol military positions, ADSBDB
|
||||||
enrichment and track backfill. Vessel routes and websocket/watchdog setup live
|
enrichment and track backfill. Vessel routes and websocket/watchdog setup live
|
||||||
@@ -1940,6 +2061,9 @@ accessible name remains "Don't show this again". Control behavior is unchanged.
|
|||||||
## FIRMS source status
|
## FIRMS source status
|
||||||
|
|
||||||
The FIRMS CSV parser rejects impossible acquisition calendar dates before the proxy filters records to the trailing 24 hours, while retaining valid UTC acquisition times.
|
The FIRMS CSV parser rejects impossible acquisition calendar dates before the proxy filters records to the trailing 24 hours, while retaining valid UTC acquisition times.
|
||||||
|
The `get_active_fires` tool reports acquisition times as UTC ISO timestamps,
|
||||||
|
including zero-padded hours and minutes; missing or invalid dates and times
|
||||||
|
remain `null`, including impossible calendar dates from a custom source.
|
||||||
|
|
||||||
The FIRMS proxy records source success after appending its rows. If aggregation
|
The FIRMS proxy records source success after appending its rows. If aggregation
|
||||||
throws, that source reports failure without a contradictory success entry.
|
throws, that source reports failure without a contradictory success entry.
|
||||||
@@ -2043,10 +2167,18 @@ fetching, trailing-24-hour filtering and partial-success caching are unchanged.
|
|||||||
> `localStorage['gev:first-run-mission:v1'] === 'suppressed'`, written **only**
|
> `localStorage['gev:first-run-mission:v1'] === 'suppressed'`, written **only**
|
||||||
> by the "Don't show this again" checkbox → the per-session
|
> by the "Don't show this again" checkbox → the per-session
|
||||||
> `sessionStorage['gev:first-run-mission-session:v1'] === 'dismissed'`, written
|
> `sessionStorage['gev:first-run-mission-session:v1'] === 'dismissed'`, written
|
||||||
> by **every** close path (mission, Explore, ESC). So it returns each fresh
|
> by **every** close path (mission, Explore, ESC, the footer dismiss button). So
|
||||||
> browser session until the visitor ticks the box; clearing storage un-ticks it,
|
> it returns each fresh browser session until the visitor ticks the box; clearing
|
||||||
> which is accepted. Both stores fail open — an unreadable store still shows the
|
> storage un-ticks it, which is accepted. Both stores fail open — an unreadable
|
||||||
> launcher rather than silently swallowing first launch.
|
> store still shows the launcher rather than silently swallowing first launch.
|
||||||
|
>
|
||||||
|
> **The footer dismiss button is the keyboardless ESC.** Phones and tablets have
|
||||||
|
> no ESC key, so the footer hint is a `data-first-run-dismiss` button that calls
|
||||||
|
> the same `dismiss()` as ESC. It stays visible at every width (phones used to
|
||||||
|
> hide it, leaving a mission as the only way out) and reads DISMISS on a
|
||||||
|
> `(hover: none) and (pointer: coarse)` screen, "ESC to dismiss" otherwise. Its
|
||||||
|
> 44px touch target is a `::before` outside the layout, so the footer row keeps
|
||||||
|
> its one-line height.
|
||||||
>
|
>
|
||||||
> **What a mission may persist (do not "simplify" this).** Layer enablement is
|
> **What a mission may persist (do not "simplify" this).** Layer enablement is
|
||||||
> durable in this app (`gev:layer-state:v2`, written by
|
> durable in this app (`gev:layer-state:v2`, written by
|
||||||
@@ -2107,6 +2239,16 @@ fetching, trailing-24-hour filtering and partial-success caching are unchanged.
|
|||||||
> always exits non-zero, `1` meaning the control is healthy and `2` meaning it
|
> always exits non-zero, `1` meaning the control is healthy and `2` meaning it
|
||||||
> is not. Plus the unit pins above.
|
> is not. Plus the unit pins above.
|
||||||
|
|
||||||
|
> **2026-10-24 — a turned-off layer's memory is released:** Cesium 1.138
|
||||||
|
> keeps the draw commands past each frame's count in the opaque and other
|
||||||
|
> per-pass arrays (`View.frustumCommandsList`) and in the offscreen pick view,
|
||||||
|
> so a turned-off layer's entities stayed reachable (Data Centers kept 116
|
||||||
|
> MiB). `src/sceneCommandRelease.js`, installed in `src/app/tools.js`,
|
||||||
|
> requests one frame through `governorRequestRender` after every layer
|
||||||
|
> disable and then trims those arrays. It reads private Cesium fields;
|
||||||
|
> `src/sceneCommandRelease.test.mjs` pins them and fails if Cesium renames
|
||||||
|
> them or changes which passes it truncates.
|
||||||
|
|
||||||
> **2026-08-08 — performance waves 1+2:** the app idles via an explicit render
|
> **2026-08-08 — performance waves 1+2:** the app idles via an explicit render
|
||||||
> governor (`src/renderGovernor.js` — hold/release from every per-frame
|
> governor (`src/renderGovernor.js` — hold/release from every per-frame
|
||||||
> animator; discrete mutators call `governorRequestRender`). Any NEW
|
> animator; discrete mutators call `governorRequestRender`). Any NEW
|
||||||
@@ -3595,7 +3737,7 @@ test:track` 43 tracking invariants · headless QA harnesses under
|
|||||||
|
|
||||||
### Interface localization (October 2026)
|
### Interface localization (October 2026)
|
||||||
|
|
||||||
Shipped locales: **en** (source and fallback), **es**, **ru** and **zh-CN**. The code lives in
|
Shipped locales: **en** (source and fallback), **es**, **ru**, **zh-CN** and **zh-Hant**. The code lives in
|
||||||
`src/i18n/`. How to mark UI text: [`docs/I18N.md`](./I18N.md); translator workflow:
|
`src/i18n/`. How to mark UI text: [`docs/I18N.md`](./I18N.md); translator workflow:
|
||||||
[`docs/TRANSLATORS.md`](./TRANSLATORS.md).
|
[`docs/TRANSLATORS.md`](./TRANSLATORS.md).
|
||||||
|
|
||||||
@@ -3606,8 +3748,8 @@ Shipped locales: **en** (source and fallback), **es**, **ru** and **zh-CN**. The
|
|||||||
value). English has no catalog; every other locale is
|
value). English has no catalog; every other locale is
|
||||||
`src/i18n/locales/<code>.json`, and a missing entry falls back to English.
|
`src/i18n/locales/<code>.json`, and a missing entry falls back to English.
|
||||||
- **Locale choice.** `?lang=` → stored choice (`gev:locale:v1`) →
|
- **Locale choice.** `?lang=` → stored choice (`gev:locale:v1`) →
|
||||||
`navigator.languages` → English. Chinese tags map to `zh-CN`; tags containing
|
`navigator.languages` → English. Chinese tags default to `zh-CN`; Traditional Chinese tags
|
||||||
`Hant`, `TW`, `HK` or `MO` are skipped. `?lang=` is not stored, and share links drop
|
select `zh-Hant`. Explicit `Hans` selects `zh-CN` regardless of region. `?lang=` is not stored, and share links drop
|
||||||
it, so a recipient keeps their own language. The globe picker in the
|
it, so a recipient keeps their own language. The globe picker in the
|
||||||
bottom-right corner (`src/ui/localePicker.js`) stores the choice, drops
|
bottom-right corner (`src/ui/localePicker.js`) stores the choice, drops
|
||||||
`?lang=` (keeps it only when storage is blocked) and reloads. It asks first
|
`?lang=` (keeps it only when storage is blocked) and reloads. It asks first
|
||||||
@@ -3685,6 +3827,7 @@ its criteria cannot be silently ignored.
|
|||||||
| Street Level 📷 | Mapillary street-level imagery (vector tiles, Graph API, MapillaryJS) | `src/layers/streetLevel/` via `src/app/layers/streetLevel.js` | `/api/mapillary/status`, `/api/mapillary/tiles/coverage/{z}/{x}/{y}` (`MAPILLARY_CLIENT_TOKEN`) | camera-driven (320 ms debounce, ≤9 tiles, server memory cache 24 h) |
|
| Street Level 📷 | Mapillary street-level imagery (vector tiles, Graph API, MapillaryJS) | `src/layers/streetLevel/` via `src/app/layers/streetLevel.js` | `/api/mapillary/status`, `/api/mapillary/tiles/coverage/{z}/{x}/{y}` (`MAPILLARY_CLIENT_TOKEN`) | camera-driven (320 ms debounce, ≤9 tiles, server memory cache 24 h) |
|
||||||
| Wind 🌬 | NOAA GFS 10 m wind (keyless, 0.25°→1° grid; animated particles) | `src/data/wind.js` | `/api/wind` | 1 h (forecast cycle) |
|
| Wind 🌬 | NOAA GFS 10 m wind (keyless, 0.25°→1° grid; animated particles) | `src/data/wind.js` | `/api/wind` | 1 h (forecast cycle) |
|
||||||
| GNSS Integrity 📡 (`gnss-integrity`) | adsb.lol point snapshot, low navigation accuracy binned per 0.5° cell (GEV NIC/NACp threshold + gpsjam.org formula and bands, keyless, ODbL) | `src/layers/gnss/` via `src/app/layers/gnss.js` | `/api/gnss-integrity` | camera-driven (600 ms debounce, minimum 2.5 s between reads) + 60s poll (server cache 60 s; 30 min rolling window) |
|
| GNSS Integrity 📡 (`gnss-integrity`) | adsb.lol point snapshot, low navigation accuracy binned per 0.5° cell (GEV NIC/NACp threshold + gpsjam.org formula and bands, keyless, ODbL) | `src/layers/gnss/` via `src/app/layers/gnss.js` | `/api/gnss-integrity` | camera-driven (600 ms debounce, minimum 2.5 s between reads) + 60s poll (server cache 60 s; 30 min rolling window) |
|
||||||
|
| S2 GhostMaps 👻 (`s2-ghostmaps`) | S2 Underground's public GhostMaps repository on GitHub (KMZ intel products, keyless, CC BY-NC-SA 4.0) | `src/layers/ghostMaps/` via `src/app/layers/ghostMaps.js` | `/api/ghostmaps/catalog`, `/api/ghostmaps/events` (server-sent events), `/api/ghostmaps/file/{product}` | branch head checked every 15 s; new commits pushed to the page; 5 min catalog backstop |
|
||||||
| Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) |
|
| Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) |
|
||||||
|
|
||||||
GNSS Integrity samples a 250 nm circle around the Earth point under the screen
|
GNSS Integrity samples a 250 nm circle around the Earth point under the screen
|
||||||
@@ -3731,6 +3874,10 @@ disabled. With it, coverage draws as z11–14 sequence lines below 60 km; the pr
|
|||||||
tiles (12 MB → ~80 KB) and shares one upstream fetch between concurrent
|
tiles (12 MB → ~80 KB) and shares one upstream fetch between concurrent
|
||||||
requests for a tile.
|
requests for a tile.
|
||||||
|
|
||||||
|
Line coverage accepts a provider’s numeric `zoomFor` result or its legacy
|
||||||
|
`{zoom, kind}` shape. Overview levels and invalid zooms show the zoom-in hint
|
||||||
|
instead of silently requesting no tiles.
|
||||||
|
|
||||||
Coverage is z11–14 sequence lines below 60 km; above that nothing is drawn and
|
Coverage is z11–14 sequence lines below 60 km; above that nothing is drawn and
|
||||||
the panel says to zoom in. Tiles come from the visible ground within range of
|
the panel says to zoom in. Tiles come from the visible ground within range of
|
||||||
the camera, nearest first, at most 9 (the halves of a view across
|
the camera, nearest first, at most 9 (the halves of a view across
|
||||||
@@ -3763,6 +3910,8 @@ reports it (`keyAlternatives`), and the panel's KEY REQUIRED, its coverage
|
|||||||
line and the layer row's tooltip say to light it. The
|
line and the layer row's tooltip say to light it. The
|
||||||
viewer sits under the header with EXPAND, FIT/FILL and close above the
|
viewer sits under the header with EXPAND, FIT/FILL and close above the
|
||||||
image. SINCE is a stepped slider whose readout names the cut-off date.
|
image. SINCE is a stepped slider whose readout names the cut-off date.
|
||||||
|
The viewer caption's bearing wraps after rounding to whole degrees, so a view
|
||||||
|
facing 359.5° or more reads `0°` rather than `360°`.
|
||||||
|
|
||||||
MapillaryJS loads on the first photo. Opening one frames it with a single
|
MapillaryJS loads on the first photo. Opening one frames it with a single
|
||||||
camera flight through the application's deferred navigation (`begin` /
|
camera flight through the application's deferred navigation (`begin` /
|
||||||
@@ -4025,6 +4174,10 @@ routing it shows `Routing…`, and a router miss reads
|
|||||||
The layer holds continuous render only while a route is drawn (the dashes
|
The layer holds continuous render only while a route is drawn (the dashes
|
||||||
animate). Disable clears all state.
|
animate). Disable clears all state.
|
||||||
|
|
||||||
|
At the metre/kilometre boundary, route distance labels use the rounded
|
||||||
|
ten-metre value to select the unit, switching to kilometres at 995 metres;
|
||||||
|
a 996-metre leg displays as `1.0 km` rather than `1000 m`.
|
||||||
|
|
||||||
**Keys needed: none.** Directions works on a keyless boot, and the layer is off
|
**Keys needed: none.** Directions works on a keyless boot, and the layer is off
|
||||||
until the operator turns it on.
|
until the operator turns it on.
|
||||||
|
|
||||||
@@ -4077,6 +4230,13 @@ and as the last line of the list, because the arrival step is among the ones
|
|||||||
dropped. A route of exactly 200 is complete, and a roundabout exit after the
|
dropped. A route of exactly 200 is complete, and a roundabout exit after the
|
||||||
200th keeps folding into it.
|
200th keeps folding into it.
|
||||||
|
|
||||||
|
Directions ramp and exit text uses “on the left/right” only for a bare side;
|
||||||
|
slight and sharp modifiers read as “slightly/sharply left/right”. Lane instructions
|
||||||
|
name the left or right lane. The localized page and proxy English stay in sync.
|
||||||
|
|
||||||
|
Route duration labels select their unit after rounding seconds, switching to
|
||||||
|
minutes at 59.5 seconds; a 59.6-second leg displays as `1 min`, rather than `60 s`.
|
||||||
|
|
||||||
A step's card opens from its dot on the globe or from its line in the list, and
|
A step's card opens from its dot on the globe or from its line in the list, and
|
||||||
the list covers every step including departure and arrival, which have no dot.
|
the list covers every step including departure and arrival, which have no dot.
|
||||||
What a card needs is a resolved ground anchor: a step whose ground cell has not
|
What a card needs is a resolved ground anchor: a step whose ground cell has not
|
||||||
@@ -4189,7 +4349,9 @@ to 0.45 at two minutes, 0.20 at ten minutes and 0.08 at fifteen minutes.
|
|||||||
|
|
||||||
**Cards and DETECT.** The card presents route, mode/operator, displayed motion,
|
**Cards and DETECT.** The card presents route, mode/operator, displayed motion,
|
||||||
display delay and newest-report age, reported speed/stop, vehicle ID, and
|
display delay and newest-report age, reported speed/stop, vehicle ID, and
|
||||||
available trail duration. Its anchor follows the rendered marker; text is
|
available trail duration. Report age is rounded to whole seconds and stays in seconds while that rounded
|
||||||
|
count is below 90; otherwise it shows completed minutes calculated from the
|
||||||
|
rounded second count. Its anchor follows the rendered marker; text is
|
||||||
checked at 4 Hz and published when it changes. DETECT labels retain route,
|
checked at 4 Hz and published when it changes. DETECT labels retain route,
|
||||||
mode and motion, with the selected label suppressed beside its fuller card.
|
mode and motion, with the selected label suppressed beside its fuller card.
|
||||||
Candidate membership changes on revisions, coalesced at 4 Hz; paints read
|
Candidate membership changes on revisions, coalesced at 4 Hz; paints read
|
||||||
@@ -4217,6 +4379,8 @@ animation. Polls, surface completions and trail fades request individual renders
|
|||||||
Disable clears vehicles, detection references, requests, timers and holds;
|
Disable clears vehicles, detection references, requests, timers and holds;
|
||||||
destruction also releases collections and registrations.
|
destruction also releases collections and registrations.
|
||||||
|
|
||||||
|
The `scripts/qa-layer-visibility.mjs` gate uses the development-only registration seam and real browser tab visibility to prove periodic work pauses while hidden, explicit refresh still works, resume performs one overdue refresh, and disabled layers stay stopped.
|
||||||
|
|
||||||
**Acceptance harness.** Run against an already running development server:
|
**Acceptance harness.** Run against an already running development server:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -4303,6 +4467,9 @@ silent for five minutes are removed. Credits appear when the feed is used.
|
|||||||
coordinator, but it is not a user-visible Data Layers entry. Its visible entry
|
coordinator, but it is not a user-visible Data Layers entry. Its visible entry
|
||||||
point is the right-side `CONTEXT` chooser's `CONTACTS` mode.
|
point is the right-side `CONTEXT` chooser's `CONTACTS` mode.
|
||||||
|
|
||||||
|
Contacts distance labels select metres or kilometres after rounding to the
|
||||||
|
nearest metre, so a 999.5-metre contact displays as `1.0 km`, not `1000 m`.
|
||||||
|
|
||||||
At global scale, ambient Radio cluster badges are hard-opacity shared-host
|
At global scale, ambient Radio cluster badges are hard-opacity shared-host
|
||||||
entries: count/category updates and identity replacement do not run keyhole or
|
entries: count/category updates and identity replacement do not run keyhole or
|
||||||
enter/exit ramps. Shared collision, viewport rejection, allocation, and horizon
|
enter/exit ramps. Shared collision, viewport rejection, allocation, and horizon
|
||||||
@@ -4569,7 +4736,7 @@ current route throughout enabling, enabled, disabling, and uncertain lifecycle
|
|||||||
renders. Both routes preserve Radio power, playback, station, filter, and volume
|
renders. Both routes preserve Radio power, playback, station, filter, and volume
|
||||||
state.
|
state.
|
||||||
|
|
||||||
Bundled datasets live in `src/data/local_data/` with per-folder provenance READMEs; they are lazily loaded via `src/data/localGeojson.js` (Vite `?url` assets) when toggled on.
|
Bundled datasets live in `src/data/local_data/` with per-folder provenance READMEs; they are lazily loaded via `src/data/localGeojsonCore.js` (Vite `?url` assets) when toggled on.
|
||||||
|
|
||||||
A bundled dataset that fails to load is a broken install, not a blip, so it is
|
A bundled dataset that fails to load is a broken install, not a blip, so it is
|
||||||
never swallowed: `localGeojson.js` guards `response.ok`, reports `error` +
|
never swallowed: `localGeojson.js` guards `response.ok`, reports `error` +
|
||||||
@@ -4601,6 +4768,7 @@ silently demoting every later lookup for the session.
|
|||||||
- Selected AIS vessels use their layer-owned full-detail presentation model in the shared world-overlay host; mapped installations promote their own entry to a selected card. Both remain crisp above post-processing without duplicate selected labels; non-selected AIS cards retain their source-owned grid/visibility selection and are host-batched with other world cards.
|
- Selected AIS vessels use their layer-owned full-detail presentation model in the shared world-overlay host; mapped installations promote their own entry to a selected card. Both remain crisp above post-processing without duplicate selected labels; non-selected AIS cards retain their source-owned grid/visibility selection and are host-batched with other world cards.
|
||||||
- Space Mission ascent replay uses the compact rocket/thrust overlay only through orbit insertion. Once the replay enters its orbit phase, that vehicle glyph is replaced by a fixed-size cyan dot following the same orbit path and callout.
|
- Space Mission ascent replay uses the compact rocket/thrust overlay only through orbit insertion. Once the replay enters its orbit phase, that vehicle glyph is replaced by a fixed-size cyan dot following the same orbit path and callout.
|
||||||
- While a subject is selected, an inner keyhole compass rotates against camera heading and up to three cyan shafted bearing arrows lock to its single faint tick-marked rim, with their labels inset just inside the circle. Labels explicitly separate the geographic bearing (`BRG`) from the contact's reported course (`CRS`) so the pointer direction is not confused with aircraft heading. They point toward the nearest observed/mapped examples; each cohort displays up to ten examples while retaining the complete locally loaded in-range cohort for navigation, with three visible at a time and a ten-second page rotation shared by the panel and arrows. This distinct neutral-context color avoids implying that all context indicators are military-flight symbols. `PREVIOUS`, `FOCUS`, and `NEXT` controls navigate selection history or the next nearby cohort example through the existing tracker. NEXT uses a cycle-scoped visited set and starts a deterministic new walk after exhausting the current candidates instead of re-admitting the nearest visited contact.
|
- While a subject is selected, an inner keyhole compass rotates against camera heading and up to three cyan shafted bearing arrows lock to its single faint tick-marked rim, with their labels inset just inside the circle. Labels explicitly separate the geographic bearing (`BRG`) from the contact's reported course (`CRS`) so the pointer direction is not confused with aircraft heading. They point toward the nearest observed/mapped examples; each cohort displays up to ten examples while retaining the complete locally loaded in-range cohort for navigation, with three visible at a time and a ten-second page rotation shared by the panel and arrows. This distinct neutral-context color avoids implying that all context indicators are military-flight symbols. `PREVIOUS`, `FOCUS`, and `NEXT` controls navigate selection history or the next nearby cohort example through the existing tracker. NEXT uses a cycle-scoped visited set and starts a deterministic new walk after exhausting the current candidates instead of re-admitting the nearest visited contact.
|
||||||
|
- Awareness `BRG` and `CRS` labels wrap after rounding to whole degrees, so a direction of 359.5° displays as `000°` rather than `360°`.
|
||||||
- Installations are viewport-bounded OSM map features (`military=airfield|naval_base|range|barracks|base` and `landuse=military`), capped to a 10° non-dateline request and 700 upstream features. The proxy caches five minutes and serves a one-hour stale fallback. Empty, stale, unavailable, and zoom-too-wide states remain visibly distinct.
|
- Installations are viewport-bounded OSM map features (`military=airfield|naval_base|range|barracks|base` and `landuse=military`), capped to a 10° non-dateline request and 700 upstream features. The proxy caches five minutes and serves a one-hour stale fallback. Empty, stale, unavailable, and zoom-too-wide states remain visibly distinct.
|
||||||
- With a selected subject, Contacts hands the installation layer the subject position every refresh. The layer loads a window around the subject (see Installations and map-source guidance), because a follow or Cockpit camera never fires `moveEnd` and often looks at the horizon: a first load from a globe view used to leave the row at "?" for the whole track. The row and the Cockpit scope read `WITHIN 100 KM` instead of `CURRENT VIEWPORT ONLY`, and an empty window reads "none mapped within 100 km; not a complete 250 km survey". `scripts/qa-context-arrivals.mjs` flies synthetic aircraft over Camp Mabry and Fort Cavazos from a wide start, presses CONTACTS, Cockpit and SEARCH NEARBY SITES, and records a clip.
|
- With a selected subject, Contacts hands the installation layer the subject position every refresh. The layer loads a window around the subject (see Installations and map-source guidance), because a follow or Cockpit camera never fires `moveEnd` and often looks at the horizon: a first load from a globe view used to leave the row at "?" for the whole track. The row and the Cockpit scope read `WITHIN 100 KM` instead of `CURRENT VIEWPORT ONLY`, and an empty window reads "none mapped within 100 km; not a complete 250 km survey". `scripts/qa-context-arrivals.mjs` flies synthetic aircraft over Camp Mabry and Fort Cavazos from a wide start, presses CONTACTS, Cockpit and SEARCH NEARBY SITES, and records a clip.
|
||||||
- The selected-only visual is one static, unfilled blue circle. It marks the 250 km proximity context window only; it is not coverage or a radar/weapon envelope. Missing broadcasts and unmapped sites are explicitly not evidence of absence.
|
- The selected-only visual is one static, unfilled blue circle. It marks the 250 km proximity context window only; it is not coverage or a radar/weapon envelope. Missing broadcasts and unmapped sites are explicitly not evidence of absence.
|
||||||
@@ -4633,7 +4801,7 @@ silently demoting every later lookup for the session.
|
|||||||
multi-sensor datasheets publish a capability range, not the lens's current zoom, so those
|
multi-sensor datasheets publish a capability range, not the lens's current zoom, so those
|
||||||
cameras keep their estimate and show `SPEC FOV min–max° (CAPABILITY)` beside it. Public
|
cameras keep their estimate and show `SPEC FOV min–max° (CAPABILITY)` beside it. Public
|
||||||
camera state carries `fovSource`, `specFovMinDeg`, `specFovMaxDeg` and `spec` (#452).
|
camera state carries `fovSource`, `specFovMinDeg`, `specFovMaxDeg` and `spec` (#452).
|
||||||
- **FIRMS**: no ground clamping (zero 3D-tiles height sampling), ≤18 screen-decluttered ambient labels, click-to-inspect detail card, 2.5k/3k sprite budgets viewport-clipped by FRP.
|
- **FIRMS**: no ground clamping (zero 3D-tiles height sampling), ≤18 screen-decluttered ambient labels, click-to-inspect detail card, 2.5k/3k sprite budgets viewport-clipped by FRP. Fire-power labels share display rounding before localization: one decimal below the rounded 10 MW boundary, then whole megawatts (for example, 9.96 MW reads 10 MW).
|
||||||
- **CCTV v2 foundation:** a pitched
|
- **CCTV v2 foundation:** a pitched
|
||||||
frustum wireframe (4 corner rays + far-cap rectangle) with a monitor plane at the frustum's
|
frustum wireframe (4 corner rays + far-cap rectangle) with a monitor plane at the frustum's
|
||||||
far cap, retargeting the existing video/canvas texture pipeline. Stills are drawn at their source aspect, centered on the placeholder dark (`#01070c`), instead of being stretched to the 16:9 canvas. Manual calibration only —
|
far cap, retargeting the existing video/canvas texture pipeline. Stills are drawn at their source aspect, centered on the placeholder dark (`#01070c`), instead of being stretched to the 16:9 canvas. Manual calibration only —
|
||||||
@@ -5010,7 +5178,7 @@ and unreachable upstream (502/504) separately from road geometry.
|
|||||||
- **Contact-match ties break on hex ascending.** `track_entity` is a mutation fulfilling "follow that one", and the model's observed answer to a non-ok track result is to retry with guesses rather than ask, so the lookup always commits rather than returning an ambiguity. What it owes the caller is stability: hex is unique and always present, so the same query resolves to the same contact for as long as both are loaded.
|
- **Contact-match ties break on hex ascending.** `track_entity` is a mutation fulfilling "follow that one", and the model's observed answer to a non-ok track result is to retry with guesses rather than ask, so the lookup always commits rather than returning an ambiguity. What it owes the caller is stability: hex is unique and always present, so the same query resolves to the same contact for as long as both are loaded.
|
||||||
- **Visual grounding**: at `local` view scale with no structured identity, the client captures the Cesium canvas (≤1200px JPEG, black-frame detection, double-render for freshness) and sends it as `input_image` with a strict "do not invent labels" instruction.
|
- **Visual grounding**: at `local` view scale with no structured identity, the client captures the Cesium canvas (≤1200px JPEG, black-frame detection, double-render for freshness) and sends it as `input_image` with a strict "do not invent labels" instruction.
|
||||||
- **Context window**: only the latest viewport screenshot stays in context — the client deletes the prior image item (`conversation.item.delete`) before adding a new one (images are the most expensive item, re-billed every turn). Text history is bounded by the **server-side** `truncation: { type: 'retention_ratio', retention_ratio, token_limits.post_instructions }` set in `/api/realtime/token` (cache-friendly batched truncation). There is intentionally **no** client-side per-turn conversation-item cap — deleting from the front of history each turn busts the Realtime prompt cache. A spatially-aware summarize-and-prune policy is specced for a future iteration.
|
- **Context window**: only the latest viewport screenshot stays in context — the client deletes the prior image item (`conversation.item.delete`) before adding a new one (images are the most expensive item, re-billed every turn). Text history is bounded by the **server-side** `truncation: { type: 'retention_ratio', retention_ratio, token_limits.post_instructions }` set in `/api/realtime/token` (cache-friendly batched truncation). There is intentionally **no** client-side per-turn conversation-item cap — deleting from the front of history each turn busts the Realtime prompt cache. A spatially-aware summarize-and-prune policy is specced for a future iteration.
|
||||||
- **Model tier + spend guard** (`src/voice/voiceCost.js`, August 2026): the voice heading row carries a `STD`/`MINI` toggle and a running session-cost readout (`~$0.42`).
|
- **Model tier + spend guard** (`src/voice/voiceCost.js`, August 2026): hosts without the voice panel keep the `STD`/`MINI` toggle and running session-cost readout (`~$0.42`) in the heading. Panel hosts place these controls in Voice Settings and mirror warning, capped or unknown cost states in the heading.
|
||||||
- **Tier selection.** `standard` = `gpt-realtime-2` (default), `mini` = `gpt-realtime-2.1-mini` (~3× cheaper per audio token). The client sends `?tier=` to `/api/realtime/token`; the endpoint resolves it through the shared registry, so an unknown, empty, or hostile value falls back to `standard` rather than reaching OpenAI as a model id. Responses echo `X-GEV-Voice-Tier` / `X-GEV-Voice-Model` (plus `X-GEV-Voice-Tier-Fallback: 1` when a bogus tier was downgraded). Persisted at `godsEyeView.voiceCost.tier`.
|
- **Tier selection.** `standard` = `gpt-realtime-2` (default), `mini` = `gpt-realtime-2.1-mini` (~3× cheaper per audio token). The client sends `?tier=` to `/api/realtime/token`; the endpoint resolves it through the shared registry, so an unknown, empty, or hostile value falls back to `standard` rather than reaching OpenAI as a model id. Responses echo `X-GEV-Voice-Tier` / `X-GEV-Voice-Model` (plus `X-GEV-Voice-Tier-Fallback: 1` when a bogus tier was downgraded). Persisted at `godsEyeView.voiceCost.tier`.
|
||||||
- **Applies NEXT session.** The model is fixed when the ephemeral token is minted, so a live session always keeps the model it connected with; toggling mid-session only records the preference (the button title says so). The cost tracker's lifetime is the session's lifetime and its model binding is immutable from `start()` to `stop()` — rebuilding it on toggle would erase accrued spend and let repeated toggles bypass the cap. The tracker may only be replaced once the session is FULLY SETTLED (`isVoiceSessionSettled()`: not active **and** no data channel **and** no peer connection) — `!isActive()` alone is not enough, because the `error` status reports inactive while the transport can still deliver a late `response.done`. The toggle itself reads and writes only the persisted preference, never the live tracker.
|
- **Applies NEXT session.** The model is fixed when the ephemeral token is minted, so a live session always keeps the model it connected with; toggling mid-session only records the preference (the button title says so). The cost tracker's lifetime is the session's lifetime and its model binding is immutable from `start()` to `stop()` — rebuilding it on toggle would erase accrued spend and let repeated toggles bypass the cap. The tracker may only be replaced once the session is FULLY SETTLED (`isVoiceSessionSettled()`: not active **and** no data channel **and** no peer connection) — `!isActive()` alone is not enough, because the `error` status reports inactive while the transport can still deliver a late `response.done`. The toggle itself reads and writes only the persisted preference, never the live tracker.
|
||||||
- **Env overrides.** `OPENAI_REALTIME_MODEL` / `OPENAI_REALTIME_MODEL_MINI` remain authoritative per tier, so a drifted upstream model id is a `.env` fix rather than a code change. Because an override can point a tier at any model, the client prices against the model id the server actually echoed, **not** the tier it requested. An unrecognised id is billed at the most expensive known rates plus one console warning — under-metering is what lets a cap be overrun.
|
- **Env overrides.** `OPENAI_REALTIME_MODEL` / `OPENAI_REALTIME_MODEL_MINI` remain authoritative per tier, so a drifted upstream model id is a `.env` fix rather than a code change. Because an override can point a tier at any model, the client prices against the model id the server actually echoed, **not** the tier it requested. An unrecognised id is billed at the most expensive known rates plus one console warning — under-metering is what lets a cap be overrun.
|
||||||
@@ -5037,6 +5205,7 @@ and unreachable upstream (502/504) separately from road geometry.
|
|||||||
- HUD `SUMMARY` readout requests a five-word intelligence-style summary from `/api/openai/hud-summary` (model `OPENAI_HUD_SUMMARY_MODEL`, default `gpt-5-nano`, minimal reasoning).
|
- HUD `SUMMARY` readout requests a five-word intelligence-style summary from `/api/openai/hud-summary` (model `OPENAI_HUD_SUMMARY_MODEL`, default `gpt-5-nano`, minimal reasoning).
|
||||||
- Input is the live basemap label context (place/street/nearby-place labels + enabled layers) — the model is instructed not to infer from coordinates.
|
- Input is the live basemap label context (place/street/nearby-place labels + enabled layers) — the model is instructed not to infer from coordinates.
|
||||||
- Output is sanitized to exactly five words; falls back to the deterministic telemetry summary on error/timeout (5s abort); typewriter animation on update.
|
- Output is sanitized to exactly five words; falls back to the deterministic telemetry summary on error/timeout (5s abort); typewriter animation on update.
|
||||||
|
- A summary asked for before the camera starts moving never paints over the next view: `moveStart` marks the summary dirty and bumps its revision, as `moveEnd` does, aborts the request in flight and stops a reply still typing; neither a reply nor a fallback line (on an error, the 5 s timeout, the abort, or a failed context build) paints once the revision has moved. A dropped request forgets its signature and a timeout counts as a failure, so the next 15 s tick asks again even over the same place. An interrupted typewriter reply also forgets its signature; fully displayed answers remain reusable.
|
||||||
|
|
||||||
### Place-search providers
|
### Place-search providers
|
||||||
|
|
||||||
@@ -5102,9 +5271,22 @@ HTTP refusals and outages remain retryable. Searches share a 12-second total
|
|||||||
deadline, with Photon requests capped at six seconds each. Caller/application
|
deadline, with Photon requests capped at six seconds each. Caller/application
|
||||||
cancellation stops retries and late cache writes. Replacing a location search
|
cancellation stops retries and late cache writes. Replacing a location search
|
||||||
cancels the previous lookup; disposing its controls cancels the active lookup. Photon uses a soft proximity bias, up to five
|
cancels the previous lookup; disposing its controls cancels the active lookup. Photon uses a soft proximity bias, up to five
|
||||||
candidates, and an unbiased retry for name mismatches. Invalid/wrapped bounds
|
candidates, and an unbiased retry for name mismatches. Invalid or wrapped bounds returned by providers
|
||||||
are omitted rather than framing the wrong part of the globe.
|
are omitted rather than framing the wrong part of the globe.
|
||||||
|
|
||||||
|
A location search that finds nothing, or fails, leaves the field open with the
|
||||||
|
query still in it, focused and selected, so it can be corrected and resubmitted
|
||||||
|
without reopening search. The miss is shown under the field as a persistent
|
||||||
|
`role="alert"` message ("Location not found." or "Search failed." with what to
|
||||||
|
do next) referenced by the input's `aria-describedby`, with `aria-invalid` set;
|
||||||
|
it is not a toast. Typing, a new search, or closing the emptied field clears it.
|
||||||
|
A search that arrives still clears and closes the field.
|
||||||
|
|
||||||
|
For request viewport bias, Photon unwraps antimeridian-crossing longitude bounds
|
||||||
|
before computing their midpoint, then wraps that midpoint back into ±180 degrees.
|
||||||
|
Malformed viewport bounds add no proximity bias. Nominatim omits a crossing
|
||||||
|
request viewbox and continues searching without that geographic preference.
|
||||||
|
|
||||||
### Map Stack Switcher (June 2026)
|
### Map Stack Switcher (June 2026)
|
||||||
|
|
||||||
- `src/mapStackController.js` switches between Google Photorealistic 3D (`photoreal`, the default when a Google or ion key is present), keyless Esri World Imagery (the zero-key default landing, with keyless terrain), Bing Aerial / Aerial-with-Labels via Cesium ion world imagery (require `CESIUM_ION_TOKEN`), and OSM tile fallback. Bing Road is **retired**: it is gone from `MAP_STACKS`, from the `set_map_stack` enum, and from the voice aliases (road phrasings now resolve to OSM, the one shipped road basemap). An old `map=bing-road` link is simply an unknown id and takes `setStack()`'s existing photoreal fallback with the Google 3D tile lit — pinned live in `scripts/qa-map-source-tray.mjs`.
|
- `src/mapStackController.js` switches between Google Photorealistic 3D (`photoreal`, the default when a Google or ion key is present), keyless Esri World Imagery (the zero-key default landing, with keyless terrain), Bing Aerial / Aerial-with-Labels via Cesium ion world imagery (require `CESIUM_ION_TOKEN`), and OSM tile fallback. Bing Road is **retired**: it is gone from `MAP_STACKS`, from the `set_map_stack` enum, and from the voice aliases (road phrasings now resolve to OSM, the one shipped road basemap). An old `map=bing-road` link is simply an unknown id and takes `setStack()`'s existing photoreal fallback with the Google 3D tile lit — pinned live in `scripts/qa-map-source-tray.mjs`.
|
||||||
@@ -5169,6 +5351,7 @@ are omitted rather than framing the wrong part of the globe.
|
|||||||
- **A vertex is a lon/lat, and the mark is DRAPED.** Clicks are picked through `pickWorldFromScreen` — one of two scene helpers the annotation resolver factory (`src/annotations/resolver.js`) exposes alongside `sampleGroundHeight`, so the app has ONE depth-aware pick cascade rather than a second written beside it — which returns the height the pick landed on as well as the coordinate, so the live preview hangs on the roof or hillside under the pointer instead of sinking to sea level. The world renderer draws areas and routes with `clampToGround`, so the height is dropped at finish in one place (`finishSpec`) rather than being carried downstream and discarded silently: what you get is the outline you drew, lying on the surface beneath it.
|
- **A vertex is a lon/lat, and the mark is DRAPED.** Clicks are picked through `pickWorldFromScreen` — one of two scene helpers the annotation resolver factory (`src/annotations/resolver.js`) exposes alongside `sampleGroundHeight`, so the app has ONE depth-aware pick cascade rather than a second written beside it — which returns the height the pick landed on as well as the coordinate, so the live preview hangs on the roof or hillside under the pointer instead of sinking to sea level. The world renderer draws areas and routes with `clampToGround`, so the height is dropped at finish in one place (`finishSpec`) rather than being carried downstream and discarded silently: what you get is the outline you drew, lying on the surface beneath it.
|
||||||
- **Draped marks classify onto `ClassificationType.BOTH`** — terrain AND 3D tiles — not onto 3D tiles alone. With only `CESIUM_3D_TILE`, every area fill, area outline, route and arrow rendered to nothing on a keyless boot, where Cesium's own globe carries Esri/OSM imagery and there are no tiles to classify against; the labels appeared and the geometry did not. That hit spoken annotations exactly as hard as hand-drawn ones. `scripts/qa-draw-tool.mjs` counts the mark's own colour in the rendered frame before and after a shape is finished, on a keyed AND a keyless server, so "the board says there is a mark" can no longer pass for "the mark is on screen".
|
- **Draped marks classify onto `ClassificationType.BOTH`** — terrain AND 3D tiles — not onto 3D tiles alone. With only `CESIUM_3D_TILE`, every area fill, area outline, route and arrow rendered to nothing on a keyless boot, where Cesium's own globe carries Esri/OSM imagery and there are no tiles to classify against; the labels appeared and the geometry did not. That hit spoken annotations exactly as hard as hand-drawn ones. `scripts/qa-draw-tool.mjs` counts the mark's own colour in the rendered frame before and after a shape is finished, on a keyed AND a keyless server, so "the board says there is a mark" can no longer pass for "the mark is on screen".
|
||||||
- **A finished area is a CLOSED ring.** The fill polygon closes itself but the outline beside it is a polyline, so an open ring drew every edge except the one back to the first vertex — a triangle with two sides. `finishSpec` repeats the first position at the end. A line is never closed: joining its ends would invent a segment nobody drew.
|
- **A finished area is a CLOSED ring.** The fill polygon closes itself but the outline beside it is a polyline, so an open ring drew every edge except the one back to the first vertex — a triangle with two sides. `finishSpec` repeats the first position at the end. A line is never closed: joining its ends would invent a segment nobody drew.
|
||||||
|
- Drawn-line distance labels select metres or kilometres after rounding to the nearest metre, so a 999.5-metre line displays as `1.0 km` rather than `1000 m`.
|
||||||
- **Measurements and centres are computed on unwrapped longitudes** (`unwrapLongitudes` / `wrapLongitude` in `drawMode.js`, used by `ringAreaM2`, `ringCentroid` and the engine's manual-area anchor). A ~50,000 m² rectangle straddling the antimeridian reads its 0.002° width as 359.998° under raw arithmetic: it measured thousands of square kilometres and anchored on the Greenwich meridian, half a world from the shape. Path length was already safe — haversine is periodic in the longitude delta — and a test pins that so a "fix" cannot break it.
|
- **Measurements and centres are computed on unwrapped longitudes** (`unwrapLongitudes` / `wrapLongitude` in `drawMode.js`, used by `ringAreaM2`, `ringCentroid` and the engine's manual-area anchor). A ~50,000 m² rectangle straddling the antimeridian reads its 0.002° width as 359.998° under raw arithmetic: it measured thousands of square kilometres and anchored on the Greenwich meridian, half a world from the shape. Path length was already safe — haversine is periodic in the longitude delta — and a test pins that so a "fix" cannot break it.
|
||||||
- **Bounds.** One shape holds at most `MAX_VERTICES` = 512 points; two clicks within 0.5 m are one vertex (that is what makes a double-click finish without leaving a stray point); a vertex that is not a finite on-globe coordinate is refused. A shape that encloses nothing is refused rather than placed: three collinear points are not an area and a line under a metre long is not a line, and the hint says which.
|
- **Bounds.** One shape holds at most `MAX_VERTICES` = 512 points; two clicks within 0.5 m are one vertex (that is what makes a double-click finish without leaving a stray point); a vertex that is not a finite on-globe coordinate is refused. A shape that encloses nothing is refused rather than placed: three collinear points are not an area and a line under a metre long is not a line, and the hint says which.
|
||||||
- Pointer ownership: while a session is open the tool holds the single pointer claim (`claimPointer('draw')`, `src/data/inputOwnership.js`) and **every** ambient scene selection handler — the shared tracking click gesture behind flights, military and CCTV, plus vessels, satellites, launches, fires, installations, bikeshare, radio, submarine cables, the bundled GeoJSON layers and the CCTV calibration gizmo — returns before picking. A vertex over an aircraft is a vertex, not a selection.
|
- Pointer ownership: while a session is open the tool holds the single pointer claim (`claimPointer('draw')`, `src/data/inputOwnership.js`) and **every** ambient scene selection handler — the shared tracking click gesture behind flights, military and CCTV, plus vessels, satellites, launches, fires, installations, bikeshare, radio, submarine cables, the bundled GeoJSON layers and the CCTV calibration gizmo — returns before picking. A vertex over an aircraft is a vertex, not a selection.
|
||||||
@@ -5178,6 +5361,28 @@ are omitted rather than framing the wrong part of the globe.
|
|||||||
- Pure half: `src/annotations/drawMode.js` (session, minimum vertices, double-click de-dup at 0.5 m, vertex ceiling, degenerate-geometry refusal, ring closing, dateline-safe length/area/centroid, hint text). Regression surface: `src/annotations/drawMode.test.mjs` and `src/data/inputOwnership.test.mjs` (pure), `src/annotations/drawToolBehaviour.test.mjs` (the tool driven against DOM and viewer doubles — leases, borrowed input actions, teardown, the Enter key, preview heights), `src/annotations/drawTool.test.mjs` (structural: where files live, which ids the markup carries, what the docs may claim), and the rendered harness `scripts/qa-draw-tool.mjs`.
|
- Pure half: `src/annotations/drawMode.js` (session, minimum vertices, double-click de-dup at 0.5 m, vertex ceiling, degenerate-geometry refusal, ring closing, dateline-safe length/area/centroid, hint text). Regression surface: `src/annotations/drawMode.test.mjs` and `src/data/inputOwnership.test.mjs` (pure), `src/annotations/drawToolBehaviour.test.mjs` (the tool driven against DOM and viewer doubles — leases, borrowed input actions, teardown, the Enter key, preview heights), `src/annotations/drawTool.test.mjs` (structural: where files live, which ids the markup carries, what the docs may claim), and the rendered harness `scripts/qa-draw-tool.mjs`.
|
||||||
- The annotation tree is a declared ownership group: `./annotations` → `src/annotations/index.js` in `package.json`, with its module graph listed in `scripts/package-boundaries.json`, so an annotation module reaching outside that graph is a gate failure rather than a surprise in a layer's bundle. `drawMode.js`, `drawTool.js` and their tests are in `scripts/format-scope.json`.
|
- The annotation tree is a declared ownership group: `./annotations` → `src/annotations/index.js` in `package.json`, with its module graph listed in `scripts/package-boundaries.json`, so an annotation module reaching outside that graph is a gate failure rather than a surprise in a layer's bundle. `drawMode.js`, `drawTool.js` and their tests are in `scripts/format-scope.json`.
|
||||||
|
|
||||||
|
### S2 GhostMaps (October 2026)
|
||||||
|
|
||||||
|
- **What it shows.** Events ▸ **S2 GhostMaps** draws the current edition of S2 Underground's KMZ intel products from `github.com/s2underground/GhostMaps`. The products are the Common Intelligence Picture and the 30-day kinetic pull (both on by default), the Border Crisis Map, the Iran Common Intelligence Picture, Iran Kinetic Events, Yemen FLOT, and **Latest Drop**: the newest dated incident file outside those series, `0_Archive`, `Basedata` and `Reference Material`. Each product has a chip in the row. The chip choice is kept in `localStorage` (`gev.ghostmaps.products`) and falls back to the defaults when storage is blocked.
|
||||||
|
- **Editions.** `src/layers/ghostMaps/catalog.js` (pure) matches whole repository paths per product. The newest edition wins by the date written in the file name, then by `Export_N`/`vN`. Undated names, such as the fixed 30-day file, are identified by blob sha. Files over 50 MiB are left out (the 62 MB census KMZ, for example). A renamed folder drops the product rather than matching elsewhere.
|
||||||
|
- **Updates.** The repository is not ours, so there is no webhook. `server/providers/ghostMaps.js` starts with the first `/api/ghostmaps` request, so an install that never enables the layer never contacts GitHub.
|
||||||
|
- It reads the branch head from git's smart-HTTP ref advertisement (`/info/refs?service=git-upload-pack`, about 0.5 KB, outside the REST rate limit) every `GHOSTMAPS_POLL_SECONDS` (15 s by default, 5 s minimum). Failures back off from 15 s to 5 min.
|
||||||
|
- Only a new head costs REST calls: one recursive tree and one commit. An optional `GITHUB_TOKEN` raises the REST limit.
|
||||||
|
- After an upstream outage, a successful branch-head check clears the error and pushes a recovered catalog even when the commit is unchanged. Cached products stay in place; subsequent healthy unchanged checks stay silent.
|
||||||
|
- The new catalog goes to `.gev-cache/ghostmaps/catalog.json` and to every open page over server-sent events (`/api/ghostmaps/events`, 25 s heartbeat, at most 64 subscribers). Default products are downloaded at once.
|
||||||
|
- The layer reloads only products whose blob changed. The new edition's data source is added before the old one is removed. A product that leaves the catalog leaves the globe.
|
||||||
|
- The row reads `LIVE` while the stream is open. A five-minute catalog read is the backstop.
|
||||||
|
- **Files.** `/api/ghostmaps/file/{product}` serves only catalog products. Files are downloaded by commit-pinned raw URLs, so a CDN cannot return an older edition. They are cached on disk by blob sha, and blobs no current product uses are pruned. A request whose `?v=` names the current blob is `immutable`. Repository paths never reach the page.
|
||||||
|
- **Source ownership.** The application catalog receives `sources['s2-ghostmaps']` through the shared source contracts; an omitted or null source stays unavailable without opening a stream or fetching. The standalone source factory supplies the same-origin implementation and accepts replacements. Packed-image URLs are released on cancelled or failed product construction, and remain owned by a successfully loaded product until it is removed.
|
||||||
|
- **Voice and locales.** S2 can be toggled through its registered id and aliases. The model already sees the S2 GhostMaps brand in the id, and compact alias-hint separators keep the complete voice prompt within its existing 52,000-character limit; accepted aliases are unchanged. The row and reports are translated in Spanish, Russian and Simplified Chinese.
|
||||||
|
- **Rendering.** The page parses the KMZ with the import module's zip, KMZ and XML helpers into one `CustomDataSource` per product.
|
||||||
|
- Points become ground-clamped billboards with the depth test off, so they never sink into terrain or 3D tiles. Because the Google 3D map hides the globe, nothing else would stop far-side pins drawing through the planet. Pins are therefore horizon-culled with Street Level's `createHorizonCull` before their first frame and again whenever the camera moves.
|
||||||
|
- Lines and polygon outlines are ground-clamped polylines.
|
||||||
|
- Styles, StyleMaps and inline styles are honoured. Icons come only from images packed in the same KMZ and at most 512 KB. Anything else uses a per-product round marker.
|
||||||
|
- Network links, overlays, models and remote hrefs are never followed. Hidden placemarks are skipped. Bounds are 20,000 placemarks and 250,000 positions per product.
|
||||||
|
- **Reports.** Clicking a marker opens a card with the product, folder, edition date and the placemark's report fields. `src/layers/ghostMaps/records.js` reads ArcGIS popup tables as text only: `<Null>` and internal fields are dropped and values are capped. Description markup is never inserted into the page. The first `http(s)` research link opens from the card. The card is `stateless`, so it comes and goes with the click, as CCTV cards do. It is also `horizonCull`ed, so it hides when its marker turns behind the planet. Picks are registered under `s2-ghostmaps`.
|
||||||
|
- **Validation.** Unit tests: `src/layers/ghostMaps/*.test.mjs` cover editions, the catalog, ref parsing, description tables and wrapping; `server/providers/ghostMaps.test.mjs` covers commit detection, events, pinned downloads, cache pruning, restart and routing. `node scripts/qa-ghostmaps.mjs --url http://localhost:4173` covers the live load, the open stream, picks and cards (asserted on the overlay host's diagnostics, since the `desynchronized` overlay canvas can read back a stale buffer headless), far-side culling, a synthetic push, chip persistence, disable and phone width.
|
||||||
|
|
||||||
### Import Your Own Files (October 2026)
|
### Import Your Own Files (October 2026)
|
||||||
|
|
||||||
- DISPLAY ▸ **Import** reads GeoJSON (`.geojson`/`.json`), KML, KMZ and GPX from the picker or a file drop anywhere in the app. Each file gets show/hide, Fly-to and Remove controls. GeoJSON and GPX use the row's color; KML/KMZ retain their styles and show a hatched swatch. The file bytes remain on the device for the page session. Rendered overlays can appear in map screenshots sent through voice or other existing screenshot tools.
|
- DISPLAY ▸ **Import** reads GeoJSON (`.geojson`/`.json`), KML, KMZ and GPX from the picker or a file drop anywhere in the app. Each file gets show/hide, Fly-to and Remove controls. GeoJSON and GPX use the row's color; KML/KMZ retain their styles and show a hatched swatch. The file bytes remain on the device for the page session. Rendered overlays can appear in map screenshots sent through voice or other existing screenshot tools.
|
||||||
@@ -5213,7 +5418,13 @@ pins and share links already supply complete boxes.
|
|||||||
|
|
||||||
- **The TRACKED contact's 2D↔3D handoff is DEFAULT behaviour (2026-08-19), driven by camera distance alone.** It does NOT consult the DISPLAY-rail `3D` toggle, which continues to own the FLEET (the un-instanced draw-call budget stays the operator's decision). Policy lives in `src/data/trackedModelRegime.js` and is shared by both layers: enter below `TRACKED_MODEL_ENTER_ALT_M` = 150,000 m and hand back to the billboard only above `TRACKED_MODEL_EXIT_ALT_M` = 172,500 m. **The swap distance was set by playtesting on 2026-08-20:** a first pass at 1,000,000 m switched too early; 2D reads correctly at ~600 km and the handoff belongs at ~150 km. **Consequence, recorded on purpose:** the tracked contact now enters 3D NEARER than the FLEET does (`MODEL_ALT_CEIL_M` = 800,000 m, unchanged), so with the DISPLAY-rail `3D` toggle on, 150–800 km draws surrounding contacts as models while the selected one is still a glyph. Nothing double-draws (the fleet pass skips the tracked icao) and aligning the two is a fleet-side decision, deliberately out of scope. **The two thresholds are asymmetric on purpose:** a single threshold makes a tracked orbit sitting ON the boundary strobe billboard↔model as the camera's altitude wobbles across it. Do not collapse them. The latch is scoped per selection, so a new target re-evaluates against the ENTER ceiling rather than inheriting the previous target's exit band. Exactly ONE model is involved; it loads on demand when the regime opens, is HIDDEN (not released) on regime exit so re-entry has no load gap, and is released by the existing teardown on deselect/re-track/destroy. Cockpit and TR-3B suppression are unchanged. **Two invariants around it:** (a) the hysteresis latch AND the load-failure latch are per-selection state cleared by `_resetTrackedSelectionState()` in the tracking lifecycle (deselect / re-track / cross-layer / init / destroy) — the predicate's icao-change guard is defence only, since it needs a drawn frame while nothing is selected and the render governor's idle mode does not promise one; (b) on-demand loading is bounded at 3 attempts per selection with a 1.5 s backoff and one console warning naming the asset — the driver runs every `scene.preUpdate`, so an unbounded catch means a missing GLB spins load→reject at frame rate. The billboard stays the visual throughout a failed load.
|
- **The TRACKED contact's 2D↔3D handoff is DEFAULT behaviour (2026-08-19), driven by camera distance alone.** It does NOT consult the DISPLAY-rail `3D` toggle, which continues to own the FLEET (the un-instanced draw-call budget stays the operator's decision). Policy lives in `src/data/trackedModelRegime.js` and is shared by both layers: enter below `TRACKED_MODEL_ENTER_ALT_M` = 150,000 m and hand back to the billboard only above `TRACKED_MODEL_EXIT_ALT_M` = 172,500 m. **The swap distance was set by playtesting on 2026-08-20:** a first pass at 1,000,000 m switched too early; 2D reads correctly at ~600 km and the handoff belongs at ~150 km. **Consequence, recorded on purpose:** the tracked contact now enters 3D NEARER than the FLEET does (`MODEL_ALT_CEIL_M` = 800,000 m, unchanged), so with the DISPLAY-rail `3D` toggle on, 150–800 km draws surrounding contacts as models while the selected one is still a glyph. Nothing double-draws (the fleet pass skips the tracked icao) and aligning the two is a fleet-side decision, deliberately out of scope. **The two thresholds are asymmetric on purpose:** a single threshold makes a tracked orbit sitting ON the boundary strobe billboard↔model as the camera's altitude wobbles across it. Do not collapse them. The latch is scoped per selection, so a new target re-evaluates against the ENTER ceiling rather than inheriting the previous target's exit band. Exactly ONE model is involved; it loads on demand when the regime opens, is HIDDEN (not released) on regime exit so re-entry has no load gap, and is released by the existing teardown on deselect/re-track/destroy. Cockpit and TR-3B suppression are unchanged. **Two invariants around it:** (a) the hysteresis latch AND the load-failure latch are per-selection state cleared by `_resetTrackedSelectionState()` in the tracking lifecycle (deselect / re-track / cross-layer / init / destroy) — the predicate's icao-change guard is defence only, since it needs a drawn frame while nothing is selected and the render governor's idle mode does not promise one; (b) on-demand loading is bounded at 3 attempts per selection with a 1.5 s backoff and one console warning naming the asset — the driver runs every `scene.preUpdate`, so an unbounded catch means a missing GLB spins load→reject at frame rate. The billboard stays the visual throughout a failed load.
|
||||||
- **Grounded 3D handoff is terrain-validity gated (2026-08-23).** A ready civilian or military glTF does not own the visual until `groundSnap` (`src/data/groundSnap.js`) can answer with a MEASURED photoreal-surface height. On success the layer writes `height + the model's measured belly offset` before revealing the model. Cache movement is measured on the WGS84 surface, not across altitude, so a stationary contact keeps its snap through poll-time vertical-datum changes. Model existence or GPU readiness alone never suppresses the billboard floor, and ordinary zoom/style/deselect transitions preserve a valid snap cache. **Two states, and the difference is the whole design.** COLD — nothing has ever resolved for this icao (tiles still streaming on first sight, sample failure, backoff after a first miss): there is no evidence of where the ground is, `heightFor` returns null, the model stays hidden and the depth-test-free 2D billboard remains opaque and floored. WARM — a snap resolved and then a >`MOVE_INVALIDATE_M` (50 m) taxi move stopped it answering directly: the measurement is DEMOTED to a bounded last-known rather than deleted, and it keeps answering while the resample is outstanding, so a taxiing aircraft does not pop 3D→2D→3D across a 2–30 s retry backoff. The bound is `HELD_SNAP_MAX_DRIFT_M` = 250 m from the spot the value was measured at, past which the hold is dropped rather than stretched and the contact is COLD again. It is spatial with no timer beside it (ground under a contact that has not moved does not change; what invalidates the value is the contact MOVING) and deliberately a quarter of the billboard chain's `HELD_FLOOR_MAX_DRIFT_KM` — that hold only ever RAISES a sprite, while a held snap IS the model's placement, so its error shows in both directions. A fresh sample releases the hold, and so does a ground flip (which already calls `forget`). **A loading model is HIDDEN, never zero-scaled:** admission sets `show = false` (Cesium's default is `true`, and an unplaced primitive would claim the visual at the identity matrix), and ownership is `ready && show` — Cesium 1.138's `Model.update` has no `show` guard, so hiding a primitive costs its load nothing.
|
- **Grounded 3D handoff is terrain-validity gated (2026-08-23).** A ready civilian or military glTF does not own the visual until `groundSnap` (`src/data/groundSnap.js`) can answer with a MEASURED photoreal-surface height. On success the layer writes `height + the model's measured belly offset` before revealing the model. Cache movement is measured on the WGS84 surface, not across altitude, so a stationary contact keeps its snap through poll-time vertical-datum changes. Model existence or GPU readiness alone never suppresses the billboard floor, and ordinary zoom/style/deselect transitions preserve a valid snap cache. **Two states, and the difference is the whole design.** COLD — nothing has ever resolved for this icao (tiles still streaming on first sight, sample failure, backoff after a first miss): there is no evidence of where the ground is, `heightFor` returns null, the model stays hidden and the depth-test-free 2D billboard remains opaque and floored. WARM — a snap resolved and then a >`MOVE_INVALIDATE_M` (50 m) taxi move stopped it answering directly: the measurement is DEMOTED to a bounded last-known rather than deleted, and it keeps answering while the resample is outstanding, so a taxiing aircraft does not pop 3D→2D→3D across a 2–30 s retry backoff. The bound is `HELD_SNAP_MAX_DRIFT_M` = 250 m from the spot the value was measured at, past which the hold is dropped rather than stretched and the contact is COLD again. It is spatial with no timer beside it (ground under a contact that has not moved does not change; what invalidates the value is the contact MOVING) and deliberately a quarter of the billboard chain's `HELD_FLOOR_MAX_DRIFT_KM` — that hold only ever RAISES a sprite, while a held snap IS the model's placement, so its error shows in both directions. A fresh sample releases the hold, and so does a ground flip (which already calls `forget`). **A loading model is HIDDEN, never zero-scaled:** admission sets `show = false` (Cesium's default is `true`, and an unplaced primitive would claim the visual at the identity matrix), and ownership is `ready && show` — Cesium 1.138's `Model.update` has no `show` guard, so hiding a primitive costs its load nothing.
|
||||||
|
- **Ground-snap clock rollback recovery.** If the wall clock moves before the
|
||||||
|
sampling-window start, ground snap opens a new window while retaining the
|
||||||
|
normal four-samples-per-250-ms budget. Retry timestamps more than the maximum
|
||||||
|
30-second backoff ahead of the current clock are ignored. A smaller rollback
|
||||||
|
can retain a bounded retry delay of up to 30 seconds.
|
||||||
- Commercial and military aircraft use the same high-level FLEET model regime: 2D billboards when zoomed out, optional glTF models when closer, controlled by the DISPLAY rail `3D` toggle and `Proximity` / `All` modes. Since 2026-08-16 (Hangar fleet) models are PER-CLASS: real CC-BY GLBs for light/bizjet/turboprop/widebody/helicopter/uav (`CLASS_MODEL_REAL` in `src/data/aircraftClass.js` — meters-baked, scale 1, per-model belly/radius; provenance in `public/models/README.md`), the shared `airplane.glb` for the remaining civilian classes, and the military layer maps weight classes (real GLBs / 747 heavies / `jet.glb` fastjets, per-model heading offsets, flat amber or emergency red). Textured civilians carry a HEAVY tint, not a light one: `MODEL_COLOR_BLEND_AMOUNT` is `0.94` in BOTH layers under Cesium's `ColorBlendMode.MIX`, so the class colour supplies 94% of the surface and the asset's own texture ~6%. The visual direction is clean light silhouettes with only a weak diffuse contribution from the approved textures, so liveries deliberately do NOT read. IR boost raises the blend to a full `1.0`. Under NVG/FLIR (map preset or Cockpit vision) models render unlit flat-white at full alpha and scene fog is disabled (fog otherwise blacks out distant models with the globe hidden); state restores on exit.
|
- Commercial and military aircraft use the same high-level FLEET model regime: 2D billboards when zoomed out, optional glTF models when closer, controlled by the DISPLAY rail `3D` toggle and `Proximity` / `All` modes. Since 2026-08-16 (Hangar fleet) models are PER-CLASS: real CC-BY GLBs for light/bizjet/turboprop/widebody/helicopter/uav (`CLASS_MODEL_REAL` in `src/data/aircraftClass.js` — meters-baked, scale 1, per-model belly/radius; provenance in `public/models/README.md`), the shared `airplane.glb` for the remaining civilian classes, and the military layer maps weight classes (real GLBs / 747 heavies / `jet.glb` fastjets, per-model heading offsets, flat amber or emergency red). Textured civilians carry a HEAVY tint, not a light one: `MODEL_COLOR_BLEND_AMOUNT` is `0.94` in BOTH layers under Cesium's `ColorBlendMode.MIX`, so the class colour supplies 94% of the surface and the asset's own texture ~6%. The visual direction is clean light silhouettes with only a weak diffuse contribution from the approved textures, so liveries deliberately do NOT read. IR boost raises the blend to a full `1.0`. Under NVG/FLIR (map preset or Cockpit vision) models render unlit flat-white at full alpha and scene fog is disabled (fog otherwise blacks out distant models with the globe hidden); state restores on exit.
|
||||||
|
- **Aircraft models skip Cesium's dynamic environment map (2026-10-24).** Flights, Military Flights and Local ADS-B pass `environmentMapOptions: AIRCRAFT_ENVIRONMENT_MAP_OPTIONS` (`{ enabled: false }`, `src/data/aircraftModelLighting.js`) to every drawn model. Otherwise each `Model` renders the sky around it and reads it back from the GPU (`readPixels`) when it is first placed, and again whenever it moves more than 1 km, the scene's atmosphere settings change, or (under sunlight dynamic lighting) an hour of scene time passes (`DynamicEnvironmentMapManager`, Cesium 1.138). With the class colour supplying 94% of the surface, the models are lit from Cesium's default spherical harmonics instead, with no specular environment map, so the sky's model-lighting cube map no longer reaches aircraft. `src/data/aircraftModelLighting.test.mjs` pins the option at every drawn model call; the two preload calls only warm the glTF cache and are never drawn.
|
||||||
- The DISPLAY-rail `3D` toggle is the user-facing activation path for both aircraft layers. Their small approved GLBs build their render resources without Cesium's frame-spread job queue, preventing continuous Photorealistic 3D Tiles streaming from starving model readiness; model caps, tracking, camera, and fallback billboards are unchanged.
|
- The DISPLAY-rail `3D` toggle is the user-facing activation path for both aircraft layers. Their small approved GLBs build their render resources without Cesium's frame-spread job queue, preventing continuous Photorealistic 3D Tiles streaming from starving model readiness; model caps, tracking, camera, and fallback billboards are unchanged.
|
||||||
- **The `3D` toggle DEFAULTS ON in `proximity` on a first run.** Proximity is itself the budget — models appear only below `MODEL_ALT_CEIL_M` and only for the nearest `MODEL_MAX` in view — so the default costs nothing at globe scale, and `all` remains a deliberate opt-in. A fresh boot runs NO layer-state restoration (`LayerStateCoordinator.start()` returns early with neither a share payload nor stored state), so four independent initializers decide what a first-run operator sees and must agree: `booleanOption('models3d', 'e', true)` in `src/data/layerState.js`, `_models3dEnabled = true` in BOTH flight layers, `this._models3dEnabled = true` in `src/ui/aircraftDisplay.js`, and the `active` / `visible` classes on `#models3d-toggle` / `#models3d-mode-row` in `index.html`. All four are pinned together in `src/data/layerState.test.mjs`. Explicit state still wins: because the codec omits default-valued options, `models3d: false` is now what travels in a link (`lo=…f.e.0`) and restores OFF at both aircraft layers. **Consequence for returning users:** a stored `gev:layer-state:v2` blob is a FULL options snapshot, so a session that wrote one before this change carries `models3d:false` and keeps 3D off until the operator flips it (or clears the key) — the durable snapshot is treated as the recipient's own state, by design.
|
- **The `3D` toggle DEFAULTS ON in `proximity` on a first run.** Proximity is itself the budget — models appear only below `MODEL_ALT_CEIL_M` and only for the nearest `MODEL_MAX` in view — so the default costs nothing at globe scale, and `all` remains a deliberate opt-in. A fresh boot runs NO layer-state restoration (`LayerStateCoordinator.start()` returns early with neither a share payload nor stored state), so four independent initializers decide what a first-run operator sees and must agree: `booleanOption('models3d', 'e', true)` in `src/data/layerState.js`, `_models3dEnabled = true` in BOTH flight layers, `this._models3dEnabled = true` in `src/ui/aircraftDisplay.js`, and the `active` / `visible` classes on `#models3d-toggle` / `#models3d-mode-row` in `index.html`. All four are pinned together in `src/data/layerState.test.mjs`. Explicit state still wins: because the codec omits default-valued options, `models3d: false` is now what travels in a link (`lo=…f.e.0`) and restores OFF at both aircraft layers. **Consequence for returning users:** a stored `gev:layer-state:v2` blob is a FULL options snapshot, so a session that wrote one before this change carries `models3d:false` and keeps 3D off until the operator flips it (or clears the key) — the durable snapshot is treated as the recipient's own state, by design.
|
||||||
- **Consequence of the flip on the recorded tracked/fleet inversion:** the 150–800 km band where surrounding contacts draw as models while the SELECTED one is still a glyph is now what an operator sees WITHOUT arming anything. The inversion itself is unchanged and still deliberate (see `src/data/trackedModelRegime.js`); only its reachability changed.
|
- **Consequence of the flip on the recorded tracked/fleet inversion:** the 150–800 km band where surrounding contacts draw as models while the SELECTED one is still a glyph is now what an operator sees WITHOUT arming anything. The inversion itself is unchanged and still deliberate (see `src/data/trackedModelRegime.js`); only its reachability changed.
|
||||||
@@ -5268,11 +5479,21 @@ pins and share links already supply complete boxes.
|
|||||||
|
|
||||||
## Auth + Launch
|
## Auth + Launch
|
||||||
|
|
||||||
|
Vite's development filesystem deny list excludes local `.gev-logs/` and
|
||||||
|
`.gev-cache/` contents and the package checkout's root `dist/`, while allowing
|
||||||
|
dependency `dist/` assets. Checkout glob characters are escaped. This does not
|
||||||
|
apply to `public/` assets or built-preview serving.
|
||||||
|
|
||||||
|
|
||||||
- Recommended launcher: `./scripts/dev-fresh.sh` (also: `dev-secure.sh` for stricter bindings, `dev-cctv.sh` for CCTV source-pack tuning)
|
- Recommended launcher: `./scripts/dev-fresh.sh` (also: `dev-secure.sh` for stricter bindings, `dev-cctv.sh` for CCTV source-pack tuning)
|
||||||
- A successful Pinokio install writes the owner-only `pinokio/.installed`
|
- A successful Pinokio install writes the owner-only `pinokio/.installed`
|
||||||
marker. The nested launcher menu resolves that marker from its own directory:
|
marker. The nested launcher menu resolves that marker from its own directory:
|
||||||
an absent marker exposes Install, a present marker exposes Start, and a
|
an absent marker exposes Install, a present marker exposes Start, and a
|
||||||
running server with a captured ready URL exposes Open God's Eye View.
|
running server with a captured ready URL exposes Open God's Eye View. That
|
||||||
|
default item uses Pinokio's `popout` option to open the system browser; the
|
||||||
|
Server item retains the launcher console. The app's framing headers stay
|
||||||
|
unchanged. This does not assume all Pinokio desktop versions enforce those
|
||||||
|
headers in their embedded view.
|
||||||
- Build gate: `npm run build`
|
- Build gate: `npm run build`
|
||||||
- Network access: local-only by default (`HOST=localhost` in dev-fresh.sh); LAN is an explicit opt-in via `HOST=0.0.0.0` (launcher prints a key-exposure warning + LAN URL; see SECURITY.md)
|
- Network access: local-only by default (`HOST=localhost` in dev-fresh.sh); LAN is an explicit opt-in via `HOST=0.0.0.0` (launcher prints a key-exposure warning + LAN URL; see SECURITY.md)
|
||||||
- OpenSky default mode: OAuth (`OPENSKY_AUTH_MODE=oauth`; `anon` works without credentials)
|
- OpenSky default mode: OAuth (`OPENSKY_AUTH_MODE=oauth`; `anon` works without credentials)
|
||||||
@@ -5387,7 +5608,8 @@ pins and share links already supply complete boxes.
|
|||||||
- **Cockpit left-panel clearance:** the Cockpit Contact card and peripheral HUD participate in the adaptive left accordion's live obstacle measurements, including live viewport-height changes. Expanding Layers or Scenes keeps the active panel in the available upper-left corridor with internal scrolling; it does not cover the Contact card, lower Cockpit controls, or Cesium credit line. Outside Cockpit the hidden card does not alter the normal corridor.
|
- **Cockpit left-panel clearance:** the Cockpit Contact card and peripheral HUD participate in the adaptive left accordion's live obstacle measurements, including live viewport-height changes. Expanding Layers or Scenes keeps the active panel in the available upper-left corridor with internal scrolling; it does not cover the Contact card, lower Cockpit controls, or Cesium credit line. Outside Cockpit the hidden card does not alter the normal corridor.
|
||||||
- **Cockpit Context scope:** the 250 km radius applies to the air/sea proximity cohorts. Installation counts come only from the currently loaded viewport and are labeled `CURRENT VIEWPORT ONLY` in the cockpit as well as the normal Context panel; neither surface presents them as a complete 250 km installation survey.
|
- **Cockpit Context scope:** the 250 km radius applies to the air/sea proximity cohorts. Installation counts come only from the currently loaded viewport and are labeled `CURRENT VIEWPORT ONLY` in the cockpit as well as the normal Context panel; neither surface presents them as a complete 250 km installation survey.
|
||||||
- **Cockpit camera anchor:** first-person mode does not write feed-boundary corrections directly into the camera. A cockpit-only inertial anchor advances from the selected aircraft's displayed course and speed, then converges toward the authoritative delayed track with correction capped below forward motion. The displayed kinematics are derived from the same consecutive fix segment as the rendered position, with raw feed speed/course used only as fallback; a transient zero/missing feed speed therefore cannot freeze a visibly moving aircraft after layer enable or a map/cockpit handoff. Rendered altitude continues to come from that interpolated track position. Late ADS-B fixes and short render stalls can remove drift without accelerating or reversing the view. Camera placement runs before scene update/culling at a bounded 20 Hz so a moving cockpit does not force Photoreal 3D Tiles to retraverse on every display frame; textual instruments update at 10 Hz and context/layout work at 4 Hz. Every far Cockpit contact pip shares one stable Cesium texture-atlas entry and skips unused screen-projected course calculations, while only in-range 2D aircraft silhouettes pay the screen-projected rotation cost; ambient glTF collections are hidden/retained rather than synchronously destroyed at cockpit entry, and context rails lay out only on explicit content/state changes and viewport resize. The deliberate 15/30-second layer interpolation delays and per-Cesium-frame position caches remain unchanged.
|
- **Cockpit camera anchor:** first-person mode does not write feed-boundary corrections directly into the camera. A cockpit-only inertial anchor advances from the selected aircraft's displayed course and speed, then converges toward the authoritative delayed track with correction capped below forward motion. The displayed kinematics are derived from the same consecutive fix segment as the rendered position, with raw feed speed/course used only as fallback; a transient zero/missing feed speed therefore cannot freeze a visibly moving aircraft after layer enable or a map/cockpit handoff. Rendered altitude continues to come from that interpolated track position. Late ADS-B fixes and short render stalls can remove drift without accelerating or reversing the view. Camera placement runs before scene update/culling at a bounded 20 Hz so a moving cockpit does not force Photoreal 3D Tiles to retraverse on every display frame; textual instruments update at 10 Hz and context/layout work at 4 Hz. Every far Cockpit contact pip shares one stable Cesium texture-atlas entry and skips unused screen-projected course calculations, while only in-range 2D aircraft silhouettes pay the screen-projected rotation cost; ambient glTF collections are hidden/retained rather than synchronously destroyed at cockpit entry, and context rails lay out only on explicit content/state changes and viewport resize. The deliberate 15/30-second layer interpolation delays and per-Cesium-frame position caches remain unchanged.
|
||||||
- **Cockpit route, vision, and view controls:** visible on-screen `COCKPIT`, `RESET`, and `EXIT COCKPIT` controls replace reliance on the `C` shortcut. RESET uses the same canonical globe route as the map and voice actions, exits Cockpit, and releases its camera ownership rather than exposing the hidden map-style top action. When the tracked commercial flight has a plausible ADSBDB route, the top of the right briefing rail shows a compact `FROM → TO` airport strip and the visor shows a centered estimated-destination chevron with its relative bearing; absent or implausible route data hides the strip and cue rather than guessing. The cockpit-local vision control is an interactive `PREV / CURRENT / NEXT` carousel over `NORMAL`, `CRT`, `NVG`, `FLIR`, `ANIME`, `NOIR`, and `SNOW`; its previous/next actions wrap, and activating the current value advances to the next style. Cockpit enters on the active map preset, so a Cyber FLIR map opens Cockpit on FLIR while Normal remains a distinct option instead of producing a second FLIR entry. Cockpit choices temporarily activate the existing Cesium post-process stages, while exiting Cockpit through either EXIT or RESET restores the pre-entry map style. The regional-news page uses a free Google News RSS locality query first, with the existing GDELT query retained only as a fail-soft fallback; linked headlines remain reporting, not verified incidents or risk intelligence.
|
- **Cockpit route, vision, and view controls:** visible on-screen `COCKPIT`, `RESET`, and `EXIT COCKPIT` controls replace reliance on the `C` shortcut. RESET uses the same canonical globe route as the map and voice actions, exits Cockpit, and releases its camera ownership rather than exposing the hidden map-style top action. When the tracked commercial flight has a plausible ADSBDB route, the top of the right briefing rail shows a compact `FROM → TO` airport strip and the visor shows a centered estimated-destination chevron with its relative bearing; its destination label rounds to whole degrees in 000–359, wrapping rounded north to 000° while the chevron continues to use the relative bearing independently of label rounding; absent or implausible route data hides the strip and cue rather than guessing. The cockpit-local vision control is an interactive `PREV / CURRENT / NEXT` carousel over `NORMAL`, `CRT`, `NVG`, `FLIR`, `ANIME`, `NOIR`, and `SNOW`; its previous/next actions wrap, and activating the current value advances to the next style. Cockpit enters on the active map preset, so a Cyber FLIR map opens Cockpit on FLIR while Normal remains a distinct option instead of producing a second FLIR entry. Cockpit choices temporarily activate the existing Cesium post-process stages, while exiting Cockpit through either EXIT or RESET restores the pre-entry map style. The regional-news page uses a free Google News RSS locality query first, with the existing GDELT query retained only as a fail-soft fallback; linked headlines remain reporting, not verified incidents or risk intelligence.
|
||||||
|
- Cockpit wind-direction labels wrap after rounding to whole degrees, so a direction of 359.5° displays as `N · 0°` rather than `N · 360°`.
|
||||||
- **Cockpit missing observations:** missing local weather observations remain unknown rather than being converted to zero or clear skies, in the briefing and in the cloud effects; a missing temperature drops the whole local weather record, including its wind and condition.
|
- **Cockpit missing observations:** missing local weather observations remain unknown rather than being converted to zero or clear skies, in the briefing and in the cloud effects; a missing temperature drops the whole local weather record, including its wind and condition.
|
||||||
- **Cockpit weather status:** the earlier multi-canvas atmospheric compositor remains fail-closed and is not attached to the live viewer. Cockpit clouds are a separate transparent WebGL pass with a capped 520×320 framebuffer, 24 ray steps, three FBM octaves, and a 12 FPS ceiling. It defaults off and starts only when local storage explicitly contains the persisted `WX ON` opt-in (`'1'`). When opted in, observations refresh after five minutes or 25 km of aircraft movement, fail transparent when unavailable or clear, and stop on exit or disable. `WX OFF` governs atmospheric rendering only: the briefing still fetches source-backed Natural Earth region, headline, and Open-Meteo local-information data, aborting and replacing any in-flight request when the selected aircraft changes. No weather effect runs in map mode and no synthetic fallback is shown.
|
- **Cockpit weather status:** the earlier multi-canvas atmospheric compositor remains fail-closed and is not attached to the live viewer. Cockpit clouds are a separate transparent WebGL pass with a capped 520×320 framebuffer, 24 ray steps, three FBM octaves, and a 12 FPS ceiling. It defaults off and starts only when local storage explicitly contains the persisted `WX ON` opt-in (`'1'`). When opted in, observations refresh after five minutes or 25 km of aircraft movement, fail transparent when unavailable or clear, and stop on exit or disable. `WX OFF` governs atmospheric rendering only: the briefing still fetches source-backed Natural Earth region, headline, and Open-Meteo local-information data, aborting and replacing any in-flight request when the selected aircraft changes. No weather effect runs in map mode and no synthetic fallback is shown.
|
||||||
- **Cockpit trail visibility:** entering cockpit hides the selected aircraft's trail body and head so they cannot cross the first-person view; exit restores them. This cockpit-only presentation change does not alter the normal map-mode invariant that aircraft trails render through terrain using their depth-fail material.
|
- **Cockpit trail visibility:** entering cockpit hides the selected aircraft's trail body and head so they cannot cross the first-person view; exit restores them. This cockpit-only presentation change does not alter the normal map-mode invariant that aircraft trails render through terrain using their depth-fail material.
|
||||||
@@ -5407,10 +5629,11 @@ the `_detectionUserOverridden` / explicit-intent flags that would suppress a
|
|||||||
separate landed behaviour. Pinned in `src/reasonableDefaults.test.mjs` (feather,
|
separate landed behaviour. Pinned in `src/reasonableDefaults.test.mjs` (feather,
|
||||||
detection, OUTSIDE opacity) and `src/data/layerState.test.mjs` (3D).
|
detection, OUTSIDE opacity) and `src/data/layerState.test.mjs` (3D).
|
||||||
|
|
||||||
**The 2026-08-24 defaults** (superseding the interim 08-23 values of 8%/3%):
|
**Current defaults** (scope feather revised to 100% on 2026-10-25;
|
||||||
|
detection and outside opacity retain the 2026-08-24 defaults):
|
||||||
the first-run look is Detection DENSE
|
the first-run look is Detection DENSE
|
||||||
`75%`, ELASTIC allocation, Fade `7%`, OUTSIDE opacity `1%`, scope feather `11%`,
|
`75%`, ELASTIC allocation, Fade `7%`, OUTSIDE opacity `1%`, Scope ON with feather
|
||||||
and 3D fleet mode PROXIMITY, with `AIRCRAFT_BRACKET_FLOOR_ANCHOR` at `0.01` so
|
`100%`, and 3D fleet mode PROXIMITY, with `AIRCRAFT_BRACKET_FLOOR_ANCHOR` at `0.01` so
|
||||||
brackets keep their approved brightness exactly at the new OUTSIDE default. Two
|
brackets keep their approved brightness exactly at the new OUTSIDE default. Two
|
||||||
terms that must never be conflated: scope FEATHER softens the black scope-mask
|
terms that must never be conflated: scope FEATHER softens the black scope-mask
|
||||||
edge; detection FADE is the label/card fading band around the keyhole. The
|
edge; detection FADE is the label/card fading band around the keyhole. The
|
||||||
@@ -5480,14 +5703,10 @@ easier to meet (detection is now on more often), but does not create it.
|
|||||||
calibrated so `AIRCRAFT_BRACKET_ALPHA_FLOOR` (0.35) lands exactly at the
|
calibrated so `AIRCRAFT_BRACKET_ALPHA_FLOOR` (0.35) lands exactly at the
|
||||||
default, and the mapping follows bracket brightness rather than slider
|
default, and the mapping follows bracket brightness rather than slider
|
||||||
position. The `ko` PARSE fallback stays at `5`.
|
position. The `ko` PARSE fallback stays at `5`.
|
||||||
- **Scope feather: `11%`** — a soft scope-mask edge (moved `0% → 8% → 11%`; `0%` hard crop for one day,
|
- **Scope: `ON`, feather: `100%`**. `SCOPE_FEATHER_RATIO_DEFAULT` in
|
||||||
`35%` before that). `SCOPE_FEATHER_RATIO_DEFAULT` in `src/scopeMask.js`,
|
`src/scopeMask.js` matches the slider, readout and share generator. The range
|
||||||
mirrored by `#scope-feather-slider`'s markup value AND readout and
|
is 0–200%; explicit 0% is a hard edge. Missing-`scf` links retain the historical
|
||||||
`_scopeFeatherPct` in `sharelink.js`. The slider is untouched and still spans
|
35% parse fallback.
|
||||||
0–100, and an explicit `0` is still the hard-crop path — pinned, so moving the
|
|
||||||
default cannot quietly delete it. The `scf` PARSE fallback deliberately stays
|
|
||||||
at `35`: a link predating `scf` was authored when 35 was what its author saw,
|
|
||||||
and restoring the author's view is what a share link is for.
|
|
||||||
- **3D aircraft models: `ON`, mode `proximity`** — see the 3D Aircraft section
|
- **3D aircraft models: `ON`, mode `proximity`** — see the 3D Aircraft section
|
||||||
above and the DISPLAY-rail entry below (was `OFF`).
|
above and the DISPLAY-rail entry below (was `OFF`).
|
||||||
- Style shader starting params: CRT/NVG/FLIR pixelation `1.2` (just above the native `1.0` floor); thermal/FLIR ships an optional Ironbow "Predator" palette (`palette` uniform, default `0` = accurate grayscale).
|
- Style shader starting params: CRT/NVG/FLIR pixelation `1.2` (just above the native `1.0` floor); thermal/FLIR ships an optional Ironbow "Predator" palette (`palette` uniform, default `0` = accurate grayscale).
|
||||||
@@ -5515,6 +5734,11 @@ easier to meet (detection is now on more often), but does not create it.
|
|||||||
short-way-round longitude span and wraps the padded edges, so a dateline-
|
short-way-round longitude span and wraps the padded edges, so a dateline-
|
||||||
crossing box stays its true width. Raw subtraction inflated a 0.41° metro box
|
crossing box stays its true width. Raw subtraction inflated a 0.41° metro box
|
||||||
to 86.7° and a 60° territory to 132°.
|
to 86.7° and a 60° territory to 132°.
|
||||||
|
- **Point distances take the short way round 180°.** The annotation
|
||||||
|
resolver's `approximateDistanceM` (voice "point out" proximity checks) and
|
||||||
|
`places_nearby` distances wrap the longitude difference. Raw subtraction put
|
||||||
|
Savusavu 38,148 km from a camera over the Lau Islands, so the proximity check
|
||||||
|
refused it, and sorted a place 2 km across 180° last.
|
||||||
- CCTV calibration persists at `godsEyeView.cctv.calibration.v2` (v2 rebuild;
|
- CCTV calibration persists at `godsEyeView.cctv.calibration.v2` (v2 rebuild;
|
||||||
the store was wiped clean, no import from the old `v1` key).
|
the store was wiped clean, no import from the old `v1` key).
|
||||||
- CCTV v3 floor QA pins are: zero samples for heading-only edits; zero transient
|
- CCTV v3 floor QA pins are: zero samples for heading-only edits; zero transient
|
||||||
@@ -5905,6 +6129,8 @@ callsign, class and category, adsbdb operator/type and a plausible route when
|
|||||||
known, altitude, ground speed, track, vertical rate, position age, message
|
known, altitude, ground speed, track, vertical rate, position age, message
|
||||||
count, "Heard by your receiver"); markers are not camera-followed. Positioned
|
count, "Heard by your receiver"); markers are not camera-followed. Positioned
|
||||||
aircraft join detection boxes, labelled only with a decoded callsign.
|
aircraft join detection boxes, labelled only with a decoded callsign.
|
||||||
|
The card's `TRK` label wraps after rounding to whole degrees, so a track of
|
||||||
|
359.5° displays as `TRK 0°` rather than `TRK 360°`.
|
||||||
|
|
||||||
Receivers publish one record shape from `src/sources/adsbRecords.js`
|
Receivers publish one record shape from `src/sources/adsbRecords.js`
|
||||||
(`icao`, `callsign`, `category`, `onGround`, `lat`, `lon`, `altitudeFt`,
|
(`icao`, `callsign`, `category`, `onGround`, `lat`, `lon`, `altitudeFt`,
|
||||||
@@ -6011,7 +6237,7 @@ closed. Performance and live agency reliability require recorded validation.
|
|||||||
|
|
||||||
## Wind forecast models
|
## Wind forecast models
|
||||||
|
|
||||||
Wind supports selectable GFS/IFS models with persisted model choice. Each fetch chooses the available cycle and forecast step nearest the current time; rows show both issue and valid UTC timestamps. Grid identities include model, cycle, forecast step and any requested scalar field. The six model/field cache slots each retain the last two issued grids for manifest/grid rollover, coalesce concurrent requests, retain last-good data on failures and retry no faster than once per minute. Model changes clear the old field immediately, cancel old acquisition and ignore superseded results.
|
Wind supports selectable GFS/IFS models with persisted model choice. Each fetch chooses the available cycle and forecast step nearest the current time; rows show both issue and valid UTC timestamps. Grid identities include model, cycle, forecast step and any requested scalar field. The six model/field cache slots each retain the last two issued grids for manifest/grid rollover, coalesce concurrent requests, retain last-good data on failures and retry no faster than once per minute. Model changes clear the old field immediately, cancel old acquisition and ignore superseded results. Last-good data is marked stale; the wind row shows it, and `get_wind` returns `stale` and adds "(forecast may be stale)" to its summary.
|
||||||
|
|
||||||
### Local Weather review candidate
|
### Local Weather review candidate
|
||||||
|
|
||||||
@@ -6113,3 +6339,12 @@ when their footprints touch or contain one another, never across a seam toleranc
|
|||||||
- `scripts/qa-form-fields.mjs` exercises native selections, number validity,
|
- `scripts/qa-form-fields.mjs` exercises native selections, number validity,
|
||||||
calibration commit/cancel and stale-camera/disposal guards against an installed
|
calibration commit/cancel and stale-camera/disposal guards against an installed
|
||||||
controller with fixture ports, plus default/Cyber and narrow translated layouts.
|
controller with fixture ports, plus default/Cyber and narrow translated layouts.
|
||||||
|
|
||||||
|
## Docker health reporting
|
||||||
|
|
||||||
|
The Docker image probes its local HTTP root with Node every 30 seconds, using
|
||||||
|
the launcher's parsed `PORT` (default 4173). A successful HTTP response marks
|
||||||
|
the container healthy. Checks time out after 10 seconds, with a 30-second
|
||||||
|
startup grace period and three consecutive failures before reporting unhealthy.
|
||||||
|
This reports app HTTP availability, not individual provider health, and does not
|
||||||
|
configure automatic restarts.
|
||||||
@@ -53,9 +53,8 @@ Cesium installation shared with its viewer; it must not bundle separate copies.
|
|||||||
`gods-eye-view/infrastructure/geojson` exports the lower-level
|
`gods-eye-view/infrastructure/geojson` exports the lower-level
|
||||||
`createLocalGeoJsonLayer(options, services)` and existing infrastructure overlay
|
`createLocalGeoJsonLayer(options, services)` and existing infrastructure overlay
|
||||||
helpers. `gods-eye-view/infrastructure/lod` exports the existing LOD policy helpers.
|
helpers. `gods-eye-view/infrastructure/lod` exports the existing LOD policy helpers.
|
||||||
The standalone app's `src/data/localGeojson.js` keeps its original single-argument
|
Consumers use the package exports, which do not import standalone application
|
||||||
factory and supplies its existing functions. Other consumers should use the package
|
globals.
|
||||||
exports, which do not import standalone application globals.
|
|
||||||
|
|
||||||
Dataset files and source/license notices remain under `src/data/local_data/`.
|
Dataset files and source/license notices remain under `src/data/local_data/`.
|
||||||
Their input counts are 4,351 datacenter features and 704 dam features. Cesium may
|
Their input counts are 4,351 datacenter features and 704 dam features. Cesium may
|
||||||
|
|||||||
@@ -0,0 +1,365 @@
|
|||||||
|
# Share-link format
|
||||||
|
|
||||||
|
A share link restores a view: where the camera is, what it follows, the visual
|
||||||
|
style, data layers and panel layout. There are two forms.
|
||||||
|
|
||||||
|
- **Long form** — `#v=2&lat=30.2672&lon=-97.7431&…`. Parsed by
|
||||||
|
`src/sharelink.js`. The address bar follows the live view in this form, and
|
||||||
|
links already in the wild keep working; the long form is never removed.
|
||||||
|
- **Short form** — `<app>/share/<code>`, where `<app>` is wherever the app is
|
||||||
|
served (an origin's root, or a base path) and `<code>` is encoded and
|
||||||
|
decoded by `src/share/viewCodec.js`, which is pure (no DOM, no Cesium) and
|
||||||
|
needs no server. Hosts import it as `@gods-eye-view/core/share/view-codec`
|
||||||
|
(a server can read a code to describe the view in a link preview). The share button makes this form. The same code opens on
|
||||||
|
any copy of the app. `src/share/shareLinkPath.js` reads and builds the
|
||||||
|
address, and `src/share/legacyShareState.js` converts between the long
|
||||||
|
form's parsed state and the code's state.
|
||||||
|
|
||||||
|
The same view in both forms (a real session's link):
|
||||||
|
|
||||||
|
| Form | Characters |
|
||||||
|
| ----- | ----------------- |
|
||||||
|
| Long | 302 (the hash) |
|
||||||
|
| Short | 39 (the `<code>`) |
|
||||||
|
|
||||||
|
For example, a camera-only place view is a 24-character code (31 with its
|
||||||
|
created-at time).
|
||||||
|
Following an aircraft is 42–50: 42 when the link is made from the app's own
|
||||||
|
follow camera with the fix taken as the link is made, 44 with a fix four
|
||||||
|
seconds older, and 50 after orbiting to another heading and pitch. A view in
|
||||||
|
any other style that keeps the style's own look adds only the style (two
|
||||||
|
bytes): the 42-character followed aircraft in Noir is 44.
|
||||||
|
|
||||||
|
## Opening a short link
|
||||||
|
|
||||||
|
At start-up the app reads `<code>` from an address whose path ends in
|
||||||
|
`share/<code>` (a host can pass the code instead, as `initialShareCode` to
|
||||||
|
`createApplicationControls`). A code that decodes runs the same restore as a
|
||||||
|
long link; once the app next writes the address, `/share/<code>` is replaced
|
||||||
|
by the app's own address and the long form, without a new history entry, so
|
||||||
|
a reload follows the live view. A code that is rejected restores nothing: the
|
||||||
|
app starts as usual and says the link can't be opened.
|
||||||
|
|
||||||
|
A thing view turns on its entity's layer and follows the entity by id, using
|
||||||
|
the same restore as a long link's followed flight or satellite (and its
|
||||||
|
expiry windows); a vessel is selected once the vessel feed has it. Opening is
|
||||||
|
best effort: until the entity is found, and if it is not found in time, the
|
||||||
|
camera is where the link was made, looking at the entity's last-known fix,
|
||||||
|
and the app says that the entity isn't reporting and when the fix was taken.
|
||||||
|
The share button makes a thing view while the camera follows an aircraft,
|
||||||
|
military aircraft or satellite. A selected vessel is not followed (the camera
|
||||||
|
stays where the user puts it), so a link made then is a place view; vessel
|
||||||
|
thing views open, but the app does not make them yet.
|
||||||
|
|
||||||
|
## Payload
|
||||||
|
|
||||||
|
The code is unpadded base64url (`A–Z a–z 0–9 - _`). The decoded bytes are:
|
||||||
|
|
||||||
|
1. one **version byte**, currently `1`;
|
||||||
|
2. a sequence of **fields**, each a varint tag `field_number << 3 | wire_type`
|
||||||
|
followed by its value;
|
||||||
|
3. one **check byte**: the CRC-8 of everything before it.
|
||||||
|
|
||||||
|
| Wire type | Value |
|
||||||
|
| --------- | ----------------------------------------------------------- |
|
||||||
|
| 0 varint | unsigned LEB128, at most 8 bytes, at most 2^53 − 1 |
|
||||||
|
| 1 fixed64 | 8 bytes (not used by v1; skipped) |
|
||||||
|
| 2 LEN | varint length, then that many bytes (text or a sub-message) |
|
||||||
|
| 5 fixed32 | 4 bytes (not used by v1; skipped) |
|
||||||
|
|
||||||
|
Signed values are zigzag varints (0, −1, 1, −2 → 0, 1, 2, 3). Text is printable
|
||||||
|
ASCII. Fields are written in ascending order; the decoder accepts any order.
|
||||||
|
|
||||||
|
A field whose value equals its default is **omitted**. The decoder fills every
|
||||||
|
omitted field from the same defaults table the encoder reads (for a
|
||||||
|
style-dependent field, the decoded style's defaults), so an empty
|
||||||
|
payload (code `AQc`: the version byte and its check byte) is the all-default
|
||||||
|
view.
|
||||||
|
|
||||||
|
### Check byte
|
||||||
|
|
||||||
|
The check byte is CRC-8 with polynomial x⁸ + x² + x + 1 (`0x07`), initial
|
||||||
|
value 0, no reflection and no final XOR (CRC-8/SMBUS; the check value of the
|
||||||
|
ASCII digits `123456789` is `0xF4`). A decoder that computes a different value
|
||||||
|
rejects the code (`checksum`) before reading any field, so a mistyped code
|
||||||
|
never opens a different view. A degree-8 CRC catches every error burst of up
|
||||||
|
to 8 bits; one base64url character is 6 bits, so **every single-character
|
||||||
|
typo is rejected** (a test tries every substitution at every position). Two
|
||||||
|
swapped neighbours, a dropped or an extra character are caught with a
|
||||||
|
probability of about 255 in 256 (many of them already fail as base64url).
|
||||||
|
|
||||||
|
## Field registry (top level)
|
||||||
|
|
||||||
|
| # | State key | Type | Default | Encoding |
|
||||||
|
| --- | ---------------------------- | ----- | ----------- | ------------------------------------------------------- |
|
||||||
|
| 1 | `view.kind` | enum | see below | 0 place, 1 thing |
|
||||||
|
| 2 | `lat` | coord | 0 | zigzag of degrees × 10⁴ |
|
||||||
|
| 3 | `lon` | coord | 0 | zigzag of degrees × 10⁴ |
|
||||||
|
| 4 | `alt` | sint | 800 | whole metres |
|
||||||
|
| 5 | `heading` | angle | 0 | whole degrees, see angles |
|
||||||
|
| 6 | `pitch` | sint | −35 | whole degrees |
|
||||||
|
| 7 | `roll` | angle | 0 | whole degrees |
|
||||||
|
| 8 | `sharedAtMs` | epoch | none | zigzag seconds since 2026-01-01T00:00:00Z |
|
||||||
|
| 9 | booleans | flags | 0 | bit set when the value differs from default |
|
||||||
|
| 10 | `layerState.enabledLayerIds` | LEN | none | layer bitset, see layers |
|
||||||
|
| 11 | `layerState.options` | LEN | none | packed option triples, see layers |
|
||||||
|
| 12 | `panelState` | LEN | none | packed panel entries, see panels |
|
||||||
|
| 13 | thing | LEN | none | sub-message, see thing views |
|
||||||
|
| 14 | `style` | enum | `normal` | normal, retro, surveillance, thermal, anime, noir, snow |
|
||||||
|
| 15 | `mapStack` | enum | `photoreal` | photoreal, bing-aerial, bing-labels, esri-imagery, osm |
|
||||||
|
| 16 | `hudVariant` | enum | style | tactical, operator, minimal, cyber |
|
||||||
|
| 17 | `bloomIntensity` | uint | style | |
|
||||||
|
| 18 | `bloomVersion` | uint | 2 | |
|
||||||
|
| 19 | `sharpenIntensity` | uint | style | |
|
||||||
|
| 20 | `detectionMode` | enum | style | OFF, SPARSE, BALANCED, DENSE |
|
||||||
|
| 21 | `detectionDensity` | uint | style | one of 0, 25, 50, 75, 100 |
|
||||||
|
| 22 | `detectionAllocation` | enum | style | ELASTIC, WEIGHTED |
|
||||||
|
| 23 | `detectionFadePct` | uint | style | 0–40 |
|
||||||
|
| 24 | `detectionOutsideOpacityPct` | uint | style | 0–100 |
|
||||||
|
| 25 | `scopeFeatherPct` | uint | 100 | 0–200 |
|
||||||
|
| 26 | `scopeTerminusPct` | uint | none | 94–100; absent is the adaptive terminus |
|
||||||
|
| 27 | `styleParams` | LEN | style | packed `(token, value × 100)` pairs |
|
||||||
|
|
||||||
|
Field 1 absent means `thing` when field 13 is present and `place` otherwise,
|
||||||
|
so v1 never writes it; a later view kind writes its code.
|
||||||
|
|
||||||
|
Enum codes are the list position (0, 1, 2…). A name not in the list is written
|
||||||
|
as LEN text in the same field, so the app can add a style or map before this
|
||||||
|
table learns its code. A new style's settings are encoded against its own
|
||||||
|
defaults, while a decoder that does not know the style uses Normal's; adding a
|
||||||
|
style also extends the pinned style-defaults table.
|
||||||
|
|
||||||
|
Flag bits (field 9): 0 `bloom`, 1 `sharpen`, 2 `hudVisible`, 3
|
||||||
|
`celestialRing` (each defaulting by style), 4 `scopeEnabled` (default on).
|
||||||
|
|
||||||
|
### Style defaults
|
||||||
|
|
||||||
|
A default marked **style** is what the view's style applies when it is
|
||||||
|
selected in a fresh session (`SHARE_VIEW_STYLE_DEFAULTS`): the app's
|
||||||
|
first-load baseline (`GLOBAL_POST_DEFAULTS` in `src/ui/visualPresets.js`),
|
||||||
|
with the style's preset on top (`STYLE_PRESET_DEFAULTS`), and the style's
|
||||||
|
shader parameters at the preset's values, else the shader's own. The encoder
|
||||||
|
compares against the defaults of the view's style, and the decoder fills
|
||||||
|
omitted settings from the defaults of the style it decoded (an unknown style
|
||||||
|
uses Normal's). So a view that keeps its style's look costs its camera and
|
||||||
|
style alone. (A recipient-side preference the app remembers, such as the
|
||||||
|
detection allocation, is not a style default: a sender who changed it pays
|
||||||
|
for it.) In v1:
|
||||||
|
|
||||||
|
| Setting | Every style |
|
||||||
|
| ------------------------- | ------------- |
|
||||||
|
| bloom, intensity | off, 0 |
|
||||||
|
| sharpen, intensity | on, 49 |
|
||||||
|
| HUD visible, variant | on, tactical |
|
||||||
|
| detection mode, density | DENSE, 75 |
|
||||||
|
| allocation, fade, outside | ELASTIC, 7, 1 |
|
||||||
|
| celestial ring | off |
|
||||||
|
|
||||||
|
| Style | Shader parameters (field 27 defaults) |
|
||||||
|
| ------------ | --------------------------------------------------------------- |
|
||||||
|
| normal | none |
|
||||||
|
| retro | pixelation 1, distortion 0, instability 0.42 |
|
||||||
|
| surveillance | gain 0.18, bloom 0.22, scanlineStr 0.96, pixelation 1 |
|
||||||
|
| thermal | sensitivity 0.85, bloom 0.2, mode 0.33, pixelation 1, palette 0 |
|
||||||
|
| anime | saturation 1, edgeThick 0.5 |
|
||||||
|
| noir | contrastAmt 1.2, grainAmt 0.5, vignetteAmt 0.5 |
|
||||||
|
| snow | density 0.6, wind 0.5 |
|
||||||
|
|
||||||
|
These are frozen like every other default: the codec reads them from the
|
||||||
|
app's tables today, and a test pins them, so if a table moves the old values
|
||||||
|
are frozen in the codec rather than changing what shared links mean.
|
||||||
|
|
||||||
|
### Precision
|
||||||
|
|
||||||
|
| Value | Stored as | Matches the long form |
|
||||||
|
| --------------------------- | ------------------------- | --------------------- |
|
||||||
|
| Latitude, longitude | 10⁻⁴ degree (about 11 m) | `toFixed(4)` |
|
||||||
|
| Altitude, range | whole metres | `Math.round` |
|
||||||
|
| Heading, pitch, roll | whole degrees | `Math.round` |
|
||||||
|
| Intensities and percentages | integers | `Math.round` |
|
||||||
|
| Style parameters | hundredths after clamping | `sp` (× 100) |
|
||||||
|
| Created-at and observed-at | whole seconds | `at` |
|
||||||
|
|
||||||
|
Angles: heading and roll are normalized into [0, 360) — the long form can
|
||||||
|
write `roll=360` for a camera at 359.6°, which the compact form stores as 0 —
|
||||||
|
and written as the nearer signed turn, so 350° costs one byte (−10).
|
||||||
|
When a state is encoded, out-of-range input is clamped (latitude, pitch) or
|
||||||
|
wrapped (longitude). A payload that stores a latitude or longitude beyond
|
||||||
|
±90/±180 cannot come from the encoder and is rejected.
|
||||||
|
|
||||||
|
### Layers (fields 10 and 11)
|
||||||
|
|
||||||
|
A layer's number is derived from its permanent layer-state token
|
||||||
|
(`src/data/layerStateTokenReservations.json`): a one-character token is its
|
||||||
|
index in `0–9a–z`, a two-character token is 36 + first × 36 + second. The
|
||||||
|
token ledger never reassigns a token, so layer numbers never change and new
|
||||||
|
layers need no change here.
|
||||||
|
|
||||||
|
Field 10 is a bitset: bit _n_ of byte ⌊n/8⌋ (least significant first) is set
|
||||||
|
when layer _n_ is on. Present-but-empty means "every layer off"; an absent
|
||||||
|
field leaves the recipient's layers alone (the long form's `l=` versus no `l`).
|
||||||
|
A thing view's entity layer is always on, so its bit is not written: in a
|
||||||
|
thing view, present-but-empty means "only the entity's layer", and an absent
|
||||||
|
field opens with only the entity's layer on (the app writes the field
|
||||||
|
whenever it has a layer set).
|
||||||
|
|
||||||
|
Field 11 repeats `(layer number, option key, value)` for each option that
|
||||||
|
differs from its frozen absent meaning (`SHARE_VIEW_LAYER_OPTION_ABSENT`). The
|
||||||
|
option key is `option token index × 2 + kind`: kind 0 means the value is a
|
||||||
|
zigzag varint (an option whose long-form spelling is a canonical integer),
|
||||||
|
kind 1 means a varint length and ASCII text (the long-form spelling). Values
|
||||||
|
are decoded by the layer-state codec's own option decoders.
|
||||||
|
|
||||||
|
A followed aircraft, military aircraft or satellite is a thing view in the
|
||||||
|
compact form, not a tracking option: the encoder moves it out of the layer
|
||||||
|
options, and the converter moves it back for the long form.
|
||||||
|
|
||||||
|
### Panels (field 12)
|
||||||
|
|
||||||
|
Each entry is one varint: `panel number × 16 + collapsed × 4 + pinned`, where
|
||||||
|
collapsed and pinned are 0 (no value), 1 (false) or 2 (true). Only panels that
|
||||||
|
differ from the frozen default layout are written, plus a collapsed-0 entry
|
||||||
|
for each panel the state has no opinion about (an older long-form link that
|
||||||
|
predates the panel); a panel with no entry takes its default. Values of 3 are
|
||||||
|
reserved: a decoder ignores that entry, as for collapsed 0.
|
||||||
|
|
||||||
|
| # | Panel | Long-form token | Default |
|
||||||
|
| --- | ---------------------- | --------------- | ------------------- |
|
||||||
|
| 0 | `control-panel` | `c` | collapsed, unpinned |
|
||||||
|
| 1 | `location-bar` | `l` | collapsed, unpinned |
|
||||||
|
| 2 | `data-panel` | `d` | collapsed |
|
||||||
|
| 3 | `cctv-panel` | `v` | collapsed |
|
||||||
|
| 4 | `recent-imagery-panel` | `i` | expanded |
|
||||||
|
| 5 | `street-level-panel` | `t` | collapsed |
|
||||||
|
| 6 | `radio-panel` | `r` | collapsed |
|
||||||
|
| 7 | `scene-panel` | `s` | collapsed |
|
||||||
|
| 8 | `global-context-panel` | `g` | collapsed |
|
||||||
|
| 9 | `pp-toggles` | `p` | collapsed |
|
||||||
|
| 10 | `param-slider-panel` | `m` | expanded |
|
||||||
|
|
||||||
|
### Style parameters (field 27)
|
||||||
|
|
||||||
|
Pairs of varints `(token index, value × 100)` for the parameters of the
|
||||||
|
view's style that differ from its defaults, using the long form's `sp` tokens
|
||||||
|
(`SHARE_VIEW_STYLE_PARAMS`). The decoded state always lists every parameter
|
||||||
|
of the style: the stored ones over the style's defaults. A long link without
|
||||||
|
`sp` converts to the style's defaults.
|
||||||
|
|
||||||
|
## View kinds
|
||||||
|
|
||||||
|
A **place** view (field 13 absent) opens at its camera, fields 2–7.
|
||||||
|
|
||||||
|
A **thing** view (field 13 present) follows an entity. Field 13 is a
|
||||||
|
sub-message:
|
||||||
|
|
||||||
|
| # | Field | Encoding | Default |
|
||||||
|
| --- | ----------- | -------------------------------------------------------- | ------- |
|
||||||
|
| 1 | aircraft | entity id: varint (24-bit ICAO address) or LEN text | |
|
||||||
|
| 2 | military | entity id: varint (24-bit ICAO address) or LEN text | |
|
||||||
|
| 3 | vessel | entity id: varint (MMSI) or LEN text | |
|
||||||
|
| 4 | satellite | entity id: varint (NORAD catalog number) or LEN text | |
|
||||||
|
| 5 | range | uint metres from the entity to the camera | 2000 |
|
||||||
|
| 6 | heading | angle, the camera's heading while following | 0 |
|
||||||
|
| 7 | pitch | sint whole degrees, the camera's pitch while following | −35 |
|
||||||
|
| 8 | fix lat | coord, the entity's last-known latitude | none |
|
||||||
|
| 9 | fix lon | coord, its last-known longitude | none |
|
||||||
|
| 10 | fix alt | sint metres | 0 |
|
||||||
|
| 11 | fix age | sint seconds the fix predates the created-at time | 0 |
|
||||||
|
| 12 | observed-at | epoch, when the fix was taken; only without a created-at | none |
|
||||||
|
|
||||||
|
The entity kind is the field that carries the id (its code in
|
||||||
|
`SHARE_VIEW_ENTITY_KINDS`), so it costs nothing; exactly one of fields 1–4 is
|
||||||
|
present (two is malformed). Entity ids are the ones the layers look up: an
|
||||||
|
aircraft's ICAO 24-bit address (six hex digits, stored as a number; other
|
||||||
|
forms such as TIS-B `~abc123` as text), a vessel's MMSI and a satellite's
|
||||||
|
NORAD catalog number (decimal numbers; an MMSI with a leading zero stays
|
||||||
|
text). Ids are lowercase and match `[0-9a-z~_-]{1,16}`.
|
||||||
|
|
||||||
|
Fields 5–7 are the follow offset: the camera looks along (heading, pitch) at
|
||||||
|
the entity from `range` metres away, heading clockwise from north, in the
|
||||||
|
entity's local east-north-up frame. The defaults match the app's own follow
|
||||||
|
camera, which looks north at about −35°, so only its range is written.
|
||||||
|
|
||||||
|
Fields 8–12 are the last-known fix, the entity's position when the link was
|
||||||
|
made. Fields 8 and 9 are both written whenever there is a fix; their presence
|
||||||
|
marks it. Its time is the created-at time (field 8 at the top level) minus
|
||||||
|
field 11, or field 12 when the view has no created-at time.
|
||||||
|
|
||||||
|
**The camera of a thing view with a fix is derived, not stored**: fields 2–7
|
||||||
|
are not written, and the camera is where following the fix from the offset
|
||||||
|
puts it (`followCameraFor` in `src/share/follow.js`): the camera sits
|
||||||
|
`−r·cos p·sin h`, `−r·cos p·cos h` and `−r·sin p` metres east, north and up
|
||||||
|
of the fix (heading h, pitch p, range r), on a sphere of radius 6 371 000 m,
|
||||||
|
and looks back at the fix (its heading and pitch are those of that direction
|
||||||
|
at the camera, roll 0; at follow distances they equal h and p). A
|
||||||
|
decoder ignores fields 2–7 when the fix is present. A thing view without a
|
||||||
|
fix (one converted from a long link, which records neither offset nor fix)
|
||||||
|
writes its camera in fields 2–7 like a place view.
|
||||||
|
|
||||||
|
A decoder that meets a view kind or an entity kind it does not know (an id in
|
||||||
|
a thing field it skips) opens the view as a place at its camera — the derived
|
||||||
|
one when there is a fix — and reports `fallback`.
|
||||||
|
|
||||||
|
## Decoding rules
|
||||||
|
|
||||||
|
`decodeShareView(code)` takes the bare code and never throws. It returns
|
||||||
|
`{ ok: true, state, unknownFields?, fallback? }` or `{ ok: false, reason }`.
|
||||||
|
|
||||||
|
Rejected (the whole link): input over 2048 characters, bad base64url
|
||||||
|
(padding, foreign characters, impossible length, stray bits), fewer than two
|
||||||
|
bytes, a check byte that does not match (`checksum`), an unknown version byte,
|
||||||
|
a truncated value, a varint over 8 bytes or above 2⁵³ − 1, an
|
||||||
|
overlong varint, a LEN longer than what remains (at any level, including
|
||||||
|
inside the thing sub-message), a reserved wire type, field number 0, a known
|
||||||
|
field with the wrong wire type or repeated, two entity ids in one thing, a
|
||||||
|
duplicate panel/option/style entry, non-ASCII text, and a latitude or
|
||||||
|
longitude (camera or fix) beyond ±90/±180.
|
||||||
|
|
||||||
|
Tolerated and reported in `unknownFields`: unknown fields at any level
|
||||||
|
(skipped by wire type), unknown enum codes and names (the default is used),
|
||||||
|
unknown flag bits, layers, panel values and style parameters, and layer
|
||||||
|
options the layer-state codec rejects or that are filed under a layer other
|
||||||
|
than their owner. Detection mode and allocation names outside their lists
|
||||||
|
fall back to the default rather than reaching the long form's lenient
|
||||||
|
migration. The map stack and HUD variant are open: a name outside their lists
|
||||||
|
that matches `[A-Za-z0-9_-]{1,32}` is kept as written (the app decides what
|
||||||
|
it means) and is not reported; only a name outside that grammar is. A
|
||||||
|
created-at time in the future is dropped, as the long form's `at` is.
|
||||||
|
|
||||||
|
## Changing the format
|
||||||
|
|
||||||
|
These rules keep every link that was ever shared working, in every version of
|
||||||
|
the app that can open it.
|
||||||
|
|
||||||
|
1. **Field numbers are permanent.** Never renumber, never reuse. A field that
|
||||||
|
is no longer written stays in `SHARE_VIEW_FIELDS` marked `retired: true`.
|
||||||
|
2. **A field's type and wire type are permanent.** To change one, add a new
|
||||||
|
field.
|
||||||
|
3. **Defaults are frozen meanings.** A default is what an omitted field meant
|
||||||
|
when links were written. If the app's default moves, leave the table alone:
|
||||||
|
the encoder will start writing the new value explicitly. Style defaults
|
||||||
|
are read from the app's style tables; when those move, freeze the old
|
||||||
|
values in the codec (see Style defaults). The same applies
|
||||||
|
to `SHARE_VIEW_LAYER_OPTION_ABSENT` and the panel defaults.
|
||||||
|
4. **Codes are append-only.** Enum lists, entity kinds, flag bits, panel
|
||||||
|
numbers and style parameter tokens only grow.
|
||||||
|
5. **Add, don't change.** A new setting takes the next free field number
|
||||||
|
(28 onwards; fields 1–15 have one-byte tags and are all taken). A new
|
||||||
|
boolean takes the next flag bit instead, which costs nothing when it is at
|
||||||
|
its default.
|
||||||
|
6. **New view kinds and entity kinds** get the next code. A new entity kind's
|
||||||
|
id takes the next free thing field (13 onwards), so older apps skip it and
|
||||||
|
fall back to the camera. A thing that needs more than an id (for example a
|
||||||
|
CCTV camera, a radio station or a transit vehicle) adds further thing
|
||||||
|
fields the same way.
|
||||||
|
7. **A new layer option** needs an entry in `SHARE_VIEW_LAYER_OPTION_ABSENT`
|
||||||
|
(a test fails until it has one).
|
||||||
|
8. **Bump the version byte only** for a change older decoders must refuse.
|
||||||
|
Decoders reject any version they do not know. The trailing CRC-8 check
|
||||||
|
byte belongs to every version.
|
||||||
|
9. **Derivations are frozen too.** `followCameraFor` and the follow-offset
|
||||||
|
defaults are part of the format: changing them moves the camera of every
|
||||||
|
thing link already shared.
|
||||||
|
|
||||||
|
Add a test for every change: a round trip, and a payload written with the new
|
||||||
|
field that the previous decoder still reads.
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
# Translating God's Eye View
|
# Translating God's Eye View
|
||||||
|
|
||||||
Shipped languages: **English** (source and fallback), **Spanish** (`es`), **Russian** (`ru`) and **Simplified Chinese** (`zh-CN`).
|
Shipped languages: **English** (source and fallback), **Spanish** (`es`), **Russian** (`ru`), **Simplified Chinese** (`zh-CN`) and **Traditional Chinese** (`zh-Hant`).
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
@@ -63,4 +63,4 @@ These are contracts, not copy: layer ids and registry keys, status enums and oth
|
|||||||
|
|
||||||
## Language notes
|
## Language notes
|
||||||
|
|
||||||
Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/<code>.md`: [Spanish](./translations/es.md), [Russian](./translations/ru.md), [Simplified Chinese](./translations/zh-CN.md). Read it before translating, and add a term there when you settle on one.
|
Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/<code>.md`: [Spanish](./translations/es.md), [Russian](./translations/ru.md), [Simplified Chinese](./translations/zh-CN.md), [Traditional Chinese](./translations/zh-Hant.md). Read it before translating, and add a term there when you settle on one.
|
||||||
+15
-9
@@ -3,10 +3,11 @@
|
|||||||
God's Eye View uses explicit auth modes for `/api/flights`:
|
God's Eye View uses explicit auth modes for `/api/flights`:
|
||||||
|
|
||||||
- `OPENSKY_AUTH_MODE=oauth` (default, recommended)
|
- `OPENSKY_AUTH_MODE=oauth` (default, recommended)
|
||||||
- `OPENSKY_AUTH_MODE=auto` (OAuth first, then Basic fallback)
|
- `OPENSKY_AUTH_MODE=auto` (OAuth when configured, otherwise anonymous)
|
||||||
- `OPENSKY_AUTH_MODE=basic`
|
|
||||||
- `OPENSKY_AUTH_MODE=anon`
|
- `OPENSKY_AUTH_MODE=anon`
|
||||||
|
|
||||||
|
`OPENSKY_AUTH_MODE=basic` is retired (see below).
|
||||||
|
|
||||||
Reference: OpenSky REST API docs recommend OAuth2 Client Credentials flow.
|
Reference: OpenSky REST API docs recommend OAuth2 Client Credentials flow.
|
||||||
|
|
||||||
## Quick Start (OAuth Client JSON)
|
## Quick Start (OAuth Client JSON)
|
||||||
@@ -41,13 +42,18 @@ Launchers resolve OAuth creds in this order:
|
|||||||
2. `OPENSKY_CREDENTIALS_FILE`
|
2. `OPENSKY_CREDENTIALS_FILE`
|
||||||
3. Keychain (`opensky-network` / `client_id`, `client_secret`)
|
3. Keychain (`opensky-network` / `client_id`, `client_secret`)
|
||||||
|
|
||||||
## Basic Auth (Legacy)
|
The server itself reads 1 and 2, so `npm run dev`, `npm run preview` and
|
||||||
|
Pinokio also work with only `OPENSKY_CREDENTIALS_FILE` set in `.env`, on any
|
||||||
|
platform. It reads the file on each token refresh.
|
||||||
|
|
||||||
Basic auth is still supported for now, but OpenSky is moving away from it.
|
## Basic Auth (Retired)
|
||||||
|
|
||||||
```bash
|
OpenSky stopped accepting username/password (HTTP Basic) sign-in on
|
||||||
OPENSKY_AUTH_MODE=basic ./scripts/dev-fresh.sh
|
2026-03-18; its API now takes only OAuth2 client credentials. With
|
||||||
```
|
`OPENSKY_AUTH_MODE=basic` the server no longer sends `OPENSKY_USERNAME` /
|
||||||
|
`OPENSKY_PASSWORD`: it requests anonymously (reason `basic_retired`) and
|
||||||
|
logs once what to set instead. Create an API client on your OpenSky
|
||||||
|
account page and use its `credentials.json` as above.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
@@ -61,6 +67,6 @@ Useful reasons:
|
|||||||
|
|
||||||
- `oauth_invalid_or_missing`: OAuth client not found/usable
|
- `oauth_invalid_or_missing`: OAuth client not found/usable
|
||||||
- `oauth_invalid_credentials`: OAuth client rejected by OpenSky
|
- `oauth_invalid_credentials`: OAuth client rejected by OpenSky
|
||||||
- `basic_invalid_credentials`: username/password rejected
|
- `missing_oauth_creds`: auto mode found no OAuth client
|
||||||
- `missing_basic_creds`: basic mode missing user/pass
|
- `basic_retired`: basic mode requested; answered anonymously
|
||||||
- `rate_limited`: OpenSky throttling
|
- `rate_limited`: OpenSky throttling
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Traditional Chinese (`zh-Hant`)
|
||||||
|
|
||||||
|
Catalog: `src/i18n/locales/zh-Hant.json`. General rules: [TRANSLATORS.md](../TRANSLATORS.md).
|
||||||
|
|
||||||
|
`zh-Hant` is the Simplified Chinese catalog (`zh-CN`, credited to abiyeenzo, #914, and EDIT-Aeterna, #979) written in Traditional characters with OpenCC character conversion. Wording and terminology are the same as in `zh-CN`: change a term in [zh-CN.md](./zh-CN.md) and the `zh-CN` catalog first, then convert. Only the script differs.
|
||||||
|
|
||||||
|
After `s2tw` conversion, fix the characters OpenCC picks wrongly: 年曆史 → 年歷史, 下遊 → 下游, 並道 → 併道, 發佈 → 發布, 公佈 → 公布.
|
||||||
|
|
||||||
|
## Style
|
||||||
|
|
||||||
|
- Follow [zh-CN.md](./zh-CN.md) for wording, punctuation, spacing, units and the glossary, written in Traditional characters.
|
||||||
|
- Relative-time placeholders may already include 前. In `LIVE · updated {age}`, the caller supplies the complete relative age; do not append another 前.
|
||||||
|
- The picker displays 中文(繁體) / Chinese (Traditional); explicit Hans tags select Simplified Chinese.
|
||||||
|
|
||||||
|
## Plurals
|
||||||
|
|
||||||
|
`Intl.PluralRules('zh-Hant')` uses only `other`. Keep the measure words of the `zh-CN` entry: 「{count} 架飛機」、「{count} 艘船」.
|
||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
with the sources: src/materialSymbolsSubset.test.mjs fails when a source
|
with the sources: src/materialSymbolsSubset.test.mjs fails when a source
|
||||||
names a glyph this list is missing, because a missing glyph does not draw
|
names a glyph this list is missing, because a missing glyph does not draw
|
||||||
a box — the ligature never forms and the element renders the literal word. -->
|
a box — the ligature never forms and the element renders the literal word. -->
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&icon_names=adjust,arrow_drop_down,arrow_forward,arrow_left,arrow_right,bolt,chevron_left,chevron_right,close,close_fullscreen,dark_mode,draw,east,flare,flight,layers,layers_clear,light_mode,local_fire_department,more_horiz,my_location,navigation,normal,on,open_in_full,public,radar,radio,right_panel_close,right_panel_open,rocket_launch,search,skip_next,skip_previous,upload_file,videocam,view_in_ar,visibility,visibility_off" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&icon_names=adjust,arrow_drop_down,arrow_forward,arrow_left,arrow_right,bolt,chevron_left,chevron_right,close,close_fullscreen,dark_mode,draw,east,flare,flight,layers,layers_clear,light_mode,local_fire_department,more_horiz,my_location,navigation,normal,on,open_in_full,public,radar,radio,right_panel_close,right_panel_open,rocket_launch,search,settings,skip_next,skip_previous,upload_file,videocam,view_in_ar,visibility,visibility_off" rel="stylesheet" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- gev:template scene-chrome -->
|
<!-- gev:template scene-chrome -->
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "gods-eye-view",
|
"name": "gods-eye-view",
|
||||||
"version": "0.2.1",
|
"version": "0.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "gods-eye-view",
|
"name": "gods-eye-view",
|
||||||
"version": "0.2.1",
|
"version": "0.3.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jtarrio/signals": "^0.10.0",
|
"@jtarrio/signals": "^0.10.0",
|
||||||
|
|||||||
+5
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gods-eye-view",
|
"name": "gods-eye-view",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.1",
|
"version": "0.3.0",
|
||||||
"description": "A real-time intelligence console for planet Earth — photorealistic 3D globe, live aircraft/ships/satellites/earthquakes/CCTV, and hands-free voice control. Runs in a browser.",
|
"description": "A real-time intelligence console for planet Earth — photorealistic 3D globe, live aircraft/ships/satellites/earthquakes/CCTV, and hands-free voice control. Runs in a browser.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -168,6 +168,7 @@
|
|||||||
"./layers/recent-imagery/source": "./src/layers/recentImagery/source.js",
|
"./layers/recent-imagery/source": "./src/layers/recentImagery/source.js",
|
||||||
"./layers/perimeters": "./src/layers/perimeters/index.js",
|
"./layers/perimeters": "./src/layers/perimeters/index.js",
|
||||||
"./layers/gnss": "./src/layers/gnss/index.js",
|
"./layers/gnss": "./src/layers/gnss/index.js",
|
||||||
|
"./layers/ghostmaps": "./src/layers/ghostMaps/index.js",
|
||||||
"./sources/firms-csv": "./src/data/firmsCsv.js",
|
"./sources/firms-csv": "./src/data/firmsCsv.js",
|
||||||
"./layers/firms": "./src/layers/firms/index.js",
|
"./layers/firms": "./src/layers/firms/index.js",
|
||||||
"./layers/satellites": "./src/layers/satellites/index.js",
|
"./layers/satellites": "./src/layers/satellites/index.js",
|
||||||
@@ -222,6 +223,8 @@
|
|||||||
"./data/lifecycle": "./src/data/lifecycle.js",
|
"./data/lifecycle": "./src/data/lifecycle.js",
|
||||||
"./ui/navigation": "./src/ui/navigationController.js",
|
"./ui/navigation": "./src/ui/navigationController.js",
|
||||||
"./ui/share-restoration": "./src/ui/shareRestoration.js",
|
"./ui/share-restoration": "./src/ui/shareRestoration.js",
|
||||||
|
"./share/view-codec": "./src/share/viewCodec.js",
|
||||||
|
"./share/link-path": "./src/share/shareLinkPath.js",
|
||||||
"./ui/visual-settings": "./src/ui/visualSettings.js",
|
"./ui/visual-settings": "./src/ui/visualSettings.js",
|
||||||
"./ui/panel-chrome": "./src/ui/panelChrome.js",
|
"./ui/panel-chrome": "./src/ui/panelChrome.js",
|
||||||
"./layers/flights/records": "./src/layers/flights/records.js",
|
"./layers/flights/records": "./src/layers/flights/records.js",
|
||||||
@@ -244,6 +247,7 @@
|
|||||||
"./layers/earthquakes/source": "./src/layers/earthquakes/source.js",
|
"./layers/earthquakes/source": "./src/layers/earthquakes/source.js",
|
||||||
"./layers/perimeters/source": "./src/layers/perimeters/source.js",
|
"./layers/perimeters/source": "./src/layers/perimeters/source.js",
|
||||||
"./layers/gnss/source": "./src/layers/gnss/source.js",
|
"./layers/gnss/source": "./src/layers/gnss/source.js",
|
||||||
|
"./layers/ghostmaps/source": "./src/layers/ghostMaps/source.js",
|
||||||
"./layers/firms/source": "./src/layers/firms/source.js",
|
"./layers/firms/source": "./src/layers/firms/source.js",
|
||||||
"./layers/installations/source": "./src/layers/installations/source.js",
|
"./layers/installations/source": "./src/layers/installations/source.js",
|
||||||
"./layers/launches/source": "./src/layers/launches/source.js",
|
"./layers/launches/source": "./src/layers/launches/source.js",
|
||||||
|
|||||||
+3
-1
@@ -22,8 +22,10 @@ module.exports = {
|
|||||||
if (starting) {
|
if (starting) {
|
||||||
const local = info.local('start.js');
|
const local = info.local('start.js');
|
||||||
if (local?.url) {
|
if (local?.url) {
|
||||||
|
// Open in the system browser without changing the app's framing policy.
|
||||||
|
// This also avoids relying on Pinokio's embedded browser behavior.
|
||||||
return [
|
return [
|
||||||
{ default: true, icon: 'fa-solid fa-earth-americas', text: 'Open God\'s Eye View', href: local.url },
|
{ default: true, icon: 'fa-solid fa-earth-americas', text: 'Open God\'s Eye View', href: local.url, popout: true },
|
||||||
{ icon: 'fa-solid fa-terminal', text: 'Server', href: 'start.js' },
|
{ icon: 'fa-solid fa-terminal', text: 'Server', href: 'start.js' },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,14 +153,6 @@ export function checkImportDirections(root) {
|
|||||||
file,
|
file,
|
||||||
'Actions must consume feed state without the manager facade',
|
'Actions must consume feed state without the manager facade',
|
||||||
);
|
);
|
||||||
if (
|
|
||||||
file === 'src/app/constructCatalog.js' &&
|
|
||||||
to === 'src/data/localGeojson.js'
|
|
||||||
)
|
|
||||||
report(
|
|
||||||
file,
|
|
||||||
'Catalog must use the services owner without compatibility layer construction',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|||||||
Loaded 100 of 378 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user