Commit Graph
833 Commits
Author SHA1 Message Date
Manjunath 1dbc8a4452 fix(ui): preserve Cyber and Cockpit visual state 2026-10-01 17:00:10 +05:30
Meir Michanie 41378656c6 docs(i18n): translator's guide for the en-only foundation, current-state and changelog
docs/TRANSLATORS.md is rewritten for the foundation state: English
shipped, and the locale-addition recipe front and center — the
one-PR-per-locale procedure (catalogs, registration, CATALOG_LOCALES,
pair behavior, parity gates). CURRENT-STATE.md documents the subsystem
(file map, resolution order, selector, gates, deferrals) and the
CHANGELOG records the foundation.
2026-10-01 12:41:20 +02:00
bassem chagra bfbaa88976 Merge remote-tracking branch 'origin/main' into feat/cctv-model-spec-enrichment
# Conflicts:
#	CHANGELOG.md
2026-10-01 12:24:42 +02:00
bassem chagra 0cfb795845 Merge remote-tracking branch 'origin/main' into feat/wsdot-camera-pack
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
2026-10-01 12:23:41 +02:00
bassem chagra 031df60014 Merge remote-tracking branch 'origin/main' into feat/king-county-camera-pack
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
2026-10-01 12:20:32 +02:00
Manjunath e03bf008f3 fix(ui): harden Cyber panel layout and preset restoration 2026-10-01 01:50:31 +05:30
Niccolò Saggese 5083a74413 Merge remote-tracking branch 'upstream/main' into fix/363-geocode-proxy
# Conflicts:
#	CHANGELOG.md
2026-09-29 19:16:15 +02:00
Said Turksever c4514784fc Merge branch 'main' into pr/mapillary-layer 2026-09-29 15:44:25 +01:00
ulihhhh 3bae92fce7 Merge upstream/main into feature/catalonia-cctv-cameras
# Conflicts:
#	docs/CURRENT-STATE.md
2026-09-29 10:08:59 +02:00
Bilawal Sidhu a19897ad15 feat(voice): voice core — honest layer reach, spoken summary with on-screen card, narration, point-and-ask
- Voice layer manifest (src/voice/layerManifest.js) generates the layer enums,
  aliases and analyst field hints; a test fails when a catalog layer has no entry.
- analyst_query validates fields, operators, units, values and scopes, counts up
  to 250,000 loaded records per layer, reports complete/partial/unanswered, tells
  LAYER_OFF / NOT_READY / FEED_UNAVAILABLE apart from zero, refuses mismatched
  follow-ups and never lets a superseded query replace follow-up memory. Seven
  more layers expose analyst records.
- Tool results carry a code-built say line and a display card; the voice card in
  the mic control shows captions, plan steps and the result
  (src/voice/resultDisplay.js is the one adapter). Slow tools narrate their
  current step out of band; holding Space while the assistant talks cuts it off.
  Turn spans go to the debug log; caption transcription is metered; the log
  omits what was said unless GEV_VOICE_LOG_CONTENT=1.
- Point and ask: the Space-down pointer snapshot resolves "this", "that one" and
  "around here" through 'pointer' targets, and "the second one" through
  referent:n from the last numbered list.
- Prompt states each policy once (instructions + tools 48.5k -> 46.7k chars).
- Voice benchmark harness (scripts/qa-voice-bench.mjs, scripts/voice-bench/)
  shares its phrase table and grading with scripts/qa-voice-routing.mjs.
2026-09-28 21:19:23 -05:00
f4fcf267c6 fix(osm): stop using public Overpass by default (#648) (#742)
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.

Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.

Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
2026-09-28 19:07:57 -05:00
Said TurkseverandClaude Opus 5.5 0f2cac2ca2 feat(street-level): one colour per imagery source
As more sources arrive, each one reads the same everywhere it appears: its
PROVIDERS chip, coverage lines, overview points, image cones and a single
legend swatch. The palette lives in one place, PROVIDER_COLORS in
src/layers/streetLevel/policy.js: Mapillary green (#05cb63), Panoramax
purple, Google Street View blue.

Mapillary drops its age dimming and magenta panoramas: lines and overview
points are Mapillary green, cones are green too and 360° images keep their
ring shape; the 360°/FLAT filter still separates them, and the selected
sequence stays GEV cyan.

The provider contract loses `legend`: `colors.coverage` is the source's one
colour, and the core composes the legend as one swatch per active source
(its name, its colour) followed by Selected. The chip group takes an
optional per-chip colour as a `--chip-color` CSS variable, written only
when it changes; Street Level chips show it as a swatch dot and, while on,
as the chip's border, text and a faint fill.

The feature gate checks the legend has one swatch per source plus Selected
and that the Mapillary chip carries #05cb63.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 00:03:00 +01:00
Said TurkseverandClaude Opus 5.5 d7d5bec752 fix(mapillary): tile flights own their abort, callers only wait on them
fetchTile coalesced concurrent requests for one tile onto a single
upstream fetch, but that fetch ran on the first caller's signal. When the
first request was abandoned (an older pan), every joined request lost a
tile it still needed; and a joined caller that aborted stayed subscribed
until the shared fetch finished.

A flight now owns its AbortController and counts waiters, the pattern the
weather and wind providers use. Each caller's own abort rejects that caller
at once; the upstream fetch is cancelled only when the last waiter leaves,
and a later request after that starts a fresh fetch.

Regression tests (fail on the previous tiles.js, pass now): a joined
request survives the first caller aborting with one upstream request; a
joined caller that aborts leaves at once while the first still gets the
tile; both waiters leaving cancels the owned fetch.

Reported by kvnloo in review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:53:40 +01:00
Said TurkseverandClaude Opus 5.5 ecb16b2fe2 Merge upstream/main into pr/mapillary-layer
Brings in #744 (share-link token ledger). package.json: keep the branch's
sorted scripts and add layer-token:next and layer-token:check.

Street Level's token moves from the provisional `3` to `0`: following
CONTRIBUTING, the provisional entry was removed and
`npm run layer-token:next -- street-level` assigned `0` (free digits in
order). It is added to the registry and to
src/data/layerStateTokenReservations.json, and
`npm run layer-token:check -- --base-ref upstream/main` passes (28
published, 1 new). No link with the provisional token was ever published.

Pinned token tests move with the ledger as CONTRIBUTING asks: 29
reservations, next free digit `3`, and the competing-PR and retired-digit
scenarios shift up one free digit (0->3, 3->4) so they still test the same
rules. The README share examples and the free-digit notes in
CONTRIBUTING and CURRENT-STATE now read `0`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:49:46 +01:00
Said TurkseverandClaude Opus 5.5 d3d3898dbb feat(street-level): place coverage on the bare earth on Google 3D
Draped ground polylines and clamped billboards land on the top of the
Google Photorealistic 3D mesh, so on a street under trees or an overpass
the coverage seemed to float at roof height.

At street zoom on Google 3D (camera below 1,400 m, back above 1,800 m)
the core switches to a `terrain` surface mode. groundCast.js samples the
existing terrain service on the ground-floor grid (0.001 deg), interpolates
bilinearly and adds 2 m; Mapillary lines become plain polylines at those
heights, and cones and the position marker stand on the street, so the
mesh hides what is behind buildings and trees. A tile draws draped first
and swaps to its cast lines once the heights arrive, keeping the old lines
until the new primitives are ready. Lookups are once per tile and aborted
when the tile is dropped; a geoid fallback counts as unknown and leaves
lines draped. Other map stacks and higher views are unchanged.

Providers get getSurface() and groundCaster in their context and an
optional setSurface(mode); getUIState() exposes `surface`. The QA gate
checks draped on Esri and terrain on Google 3D.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:16:54 +01:00
bassem chagra 3273fb2431 Merge branch 'main' into feat/king-county-camera-pack 2026-09-28 10:19:25 +02:00
bassem chagra 3bcd5010a3 Merge branch 'main' into feat/wsdot-camera-pack 2026-09-28 10:19:15 +02:00
bassem chagra 6625b48d5a Merge branch 'main' into feat/cctv-model-spec-enrichment 2026-09-28 10:19:01 +02:00
daikaginza 1e9c50754c Merge remote-tracking branch 'upstream/main' into fix/tracked-info-vertical-rate 2026-09-28 08:52:52 +07:00
daikaginza 420c725930 Merge remote-tracking branch 'upstream/main' into fix/gbfs-rate-limit 2026-09-28 08:51:32 +07:00
daikaginza f0e28a83cb Merge remote-tracking branch 'upstream/main' into fix/terrain-heights-bounds 2026-09-28 08:48:49 +07:00
daikaginza 936d75b69e feat(flights): publish verticalRateMps on the tracked-aircraft descriptor
`mapAnalystRecord` has always published `verticalRateMps`, but the tracked
seam — `getTrackedInfo()` / `getTrackedSubject()`, built by `_describeFlight`
— did not, so anything needing climb/descent had to reach past the public
descriptor into the raw poll record.

Both layers now map it, which matters because they store it under different
keys: the civil records write `verticalRate` (records.js), the military ones
write `verticalRateMps`. Mapping only one would leave
`resolveTrackedAircraftInfo()` handing back two different shapes, and a
consumer reading the field off a civil contact would silently read every
military contact as level flight.

Reported, never derived. `stickyNumber` seeds the field with null, so an
aircraft that has not transmitted a rate publishes null rather than a
level-flight 0 — absent is not level, and a flight-phase classifier that
cannot tell them apart calls a climbing aircraft cruise.

Tests: both layers, both halves (a reported rate arrives with its sign; an
absent one stays null). Mutation-verified — removing either field or
defaulting it to 0 turns them red.

`docs/CURRENT-STATE.md` records the descriptor field, the two record keys it
reconciles, and the reported-never-derived rule.
2026-09-27 23:40:01 +07:00
devv-shayan 881e24485c feat(voice): add ChatGPT OAuth auth option 2026-09-27 20:58:08 +05:00
daikaginza 7c7563d91b fix(gbfs): rate-limit /api/gbfs
The proxy already restricted WHERE it would go — https, an allowlisted host, a
station_information/station_status path, no redirects, a 12-second deadline and
a 5 MB body cap — but nothing restricted HOW OFTEN, so the dev server would
relay an unbounded stream of requests at a third-party bikeshare operator on
any caller's behalf, one outbound fetch and one timeout timer each.

Admit 120 requests/minute per client address, global backstop 3x, refused with
429 and `Retry-After` before the target is even parsed. The ceiling comes from
the layer's own demand: `src/layers/bikeshare/registry.js` catalogues 15
systems, station information is cached per city after its first fetch, and
station status refreshes once per city every STATUS_POLL_MS (60s) with
in-flight requests deduplicated — about 15 requests a minute with everything
in range, so 120 is eight times a maximal session.

The method, host, path, redirect, deadline and size guards are unchanged.

Refs #264
2026-09-27 22:32:36 +07:00
daikaginza be7187cea7 fix(server): bound the terrain-heights cache and its request rate
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. Nothing limited the rate, and coordinates were
checked for being finite rather than for being on Earth, so an
out-of-range pair was still a valid cache key.

Four bounds, each following a pattern this repo already uses:

- WGS-84 range is enforced in `parseTerrainPoints`, at the edge, so no
  later stage has to. Poles and the antimeridian still parse.
- The point cache holds at most 50,000 entries and evicts oldest-first,
  the shape `trackBackfillProxies` uses. Sized from measurement: this
  checkout's cache file held 6,189 entries in 1.0 MB of real use, so the
  ceiling is eight times that working set. Terrain does not move, so an
  evicted point costs one refetch and never a wrong answer.
- A cache file above 24 MB is not read back, mirroring
  `rocketLaunchesProxy`. An unmeasurable file is not treated as an
  oversized one: without stat, readFile decides, exactly as before.
- The route carries an always-on limiter of 90/min per client, 270
  global — the `/api/overpass` band, and an order of magnitude above
  measured demand (a full test:track run drove 10 requests, peak 8/min).

The ceilings are constructor options so they can be exercised as
behaviour rather than asserted as constants; production passes none.
They are destructured in the body because `proxyErrorResponses.test.mjs`
slices these proxies to the first column-zero closing brace.
2026-09-27 22:10:43 +07:00
bassem chagra cce159c93d docs(cctv): document identified-hardware specs and datasheet FOV in CHANGELOG and CURRENT-STATE
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR.
2026-09-27 12:24:13 +00:00
bassem chagra 5dfe3f0525 docs(cctv): add the King County pack to CHANGELOG, CURRENT-STATE, README and .env.example
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR, and new env controls belong in .env.example
with the other CCTV pack controls. The README and CURRENT-STATE table diffs
are column realignment from the widened source cell.
2026-09-27 12:23:23 +00:00
bassem chagra c4f18169eb fix(cctv): raise the default catalog cap to 4,500 and enforce it over every pack
The per-pack defaults already summed to 4,054 before WSDOT (4,304 with
it), past the documented 4,000 cap, so a default install was trimming.
The existing sum check in the DriveBC test listed packs by hand and
missed Calgary and DelDOT. Replace it with a check in
cctvCatalogCap.test.mjs that sums every exported DEFAULT_*_MAX_SOURCES,
and drop the Calgary test that pinned the cap at exactly 4,000.
2026-09-27 12:20:45 +00:00
bassem chagra 5b2e160e54 docs(cctv): add the WSDOT pack to CHANGELOG, CURRENT-STATE and README
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR. The README and CURRENT-STATE table diffs are
column realignment from the widened source cell.
2026-09-27 12:15:13 +00:00
Michael JohnstonandClaude Opus 5 5f8a275487 fix(providers): bound the remaining upstream body reads
Route the six proxied fetches that still buffered upstream bodies without
a limit through the shared capped readers in src/sources/httpBody.js:
FIRMS CSV, CelesTrak TLE groups, adsb.lol /v2/mil (both the OK and
relayed-status paths), TomTom flow tiles, OpenSky states/all and adsbdb
enrichment lookups. Each cap is a bound sized well above the measured
payload, not a budget.

readResponseBytesCapped gains the same optional abort signal as the text
reader, with the same cancel-on-abort plumbing and reader release, so the
TomTom tile path can share the route deadline instead of reading
arrayBuffer() unbounded. Where a route already had a deadline, the same
signal now covers the body read as well as the headers. Two polls had no
deadline at all: the adsb.lol military poll gains a 12-second one and the
OpenSky states/all exchange, including its Basic-auth retry, a 20-second
one; both miss into their existing STALE paths.

FIRMS map-key status, the OpenSky token exchange and CCTV media are left
as they are.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-26 14:40:08 -04:00
Said TurkseverandClaude Opus 5.5 c7dc3146f2 feat(street-level): camera follow only on the Google 3D map
FOLLOW puts the globe camera at eye height inside the street-level view; on
flat imagery or terrain there is nothing to stand among. The layer now takes
the application map stack controller (attachMapStackController), subscribes
to stack switches, offers FOLLOW only while the stack is 'photoreal', disables
the button with a MAP SOURCE hint elsewhere, and stops following when the
stack leaves Google 3D. Selection uninstall is a no-op when never installed.

The QA gate checks the disabled path and, when Google 3D is available, that
leaving it stops following; its caption-timeout dump now forces one render to
tell a missed update from a render fault.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 00:58:43 +01:00
Said TurkseverandClaude Opus 5.5 e6c2c812f9 fix(street-level): fit the whole photo on phones
At phone width the right rail is the band between mid-screen and the credit
line, so a 16:10 viewer plus its bar and caption was cut off. Below 720 px
the viewer's height is derived from that band (clamped 110-320 px); floating
and expanded viewers reset to auto height. The QA harness checks the photo
and caption fit at 390x844, and CURRENT-STATE documents the docking rules
and phone sizing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 21:18:40 +01:00
Said TurkseverandClaude Opus 5.5 5877f24f0b feat(street-level): movable, simplified panel with the photo first
- The viewer opens at the top of the panel, under the header, so the photo
  is on screen without scrolling; its tools (EXPAND, FIT/FILL, FOLLOW, close)
  sit in a bar above the image, clear of the provider's own overlays, and the
  caption wraps instead of truncating.
- No separate ON/OFF or OPEN NEAREST PHOTO buttons: the header pill is the
  layer switch, and a provider chip is lit only while the layer is on (the
  last lit chip switches the layer off).
- SINCE is a stepped slider (any date to the last month) whose readout names
  the cut-off date and is announced via aria-valuetext.
- The panel floats and resizes (portable spec); in a resized window the
  viewer takes the spare height and only the settings scroll. SHRINK after
  EXPAND, collapsing, or a header double-click docks it at its default size.
- FIT/FILL tooltips say what they do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 20:45:17 +01:00
Said TurkseverandClaude Opus 5.5 768f256720 Merge pr/panel-resize into pr/mapillary-layer
Bring in portable, resizable rail panels. The Street Level rail rules get
the same :not(.panel-floating) guard as CCTV's so the panel can float too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 19:49:01 +01:00
Said TurkseverandClaude Fable 5.1 33ee9bf23e docs(street-level): README, DATA_SOURCES, CURRENT-STATE, SECURITY, CHANGELOG
Describe the provider-neutral layer, the PROVIDERS chip row, the shared
filter and its share-link options, and how a new provider registers.
Drop every mention of the removed plain-English query and Anthropic key.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-25 18:37:42 +01:00
manjunath22466 d0105f1e9d fix: reconcile share-link token allocation with main
Preserve all published token assignments and add regression coverage for
all-enabled URL restoration, longer token lists, and manual provisional
token recovery after competing merges.

Builds on daikaginza's digits-first and merge-time allocation proposal.
Thanks to KnottyDyes for the review and regression-test suggestions.
2026-09-25 22:33:03 +05:30
Said TurkseverandClaude Fable 5.1 9ba6f96d87 Merge upstream/main into pr/mapillary-layer
Upstream took share token '2' for fire perimeters, so Street Level moves to
'3'. mapillary-js joins upstream's optimizeDeps include list instead of
overriding it; catalog and registry tests count the new upstream layers.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-25 17:58:27 +01:00
Said TurkseverandClaude Fable 5.1 7f65a32877 ui(mapillary): drop the keyless empty state
The panel no longer shows a separate keyless call-to-action; the fieldset
gate and the layer-row tooltip already cover the missing-key case. The
planner flag is tri-state (null until status answers).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-25 17:51:58 +01:00
ulihhhh 21a86c05bd Merge upstream/main into feature/catalonia-cctv-cameras 2026-09-25 12:01:51 +02:00
Sameh KhamisandClaude Opus 5.5 303429a421 docs: record estimated CCTV bearings in CHANGELOG and CURRENT-STATE (#643)
Credit the contributor and describe the HUD tag, dashed wireframe and
calibration precedence for #639.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 22:44:30 -07:00
Sameh KhamisandClaude Opus 5.5 31224d8bd2 fix(traffic): name the proxy's own 502/503 accurately (#665)
502 (every mirror unreachable) and 503 (the local limiter, before any mirror
is asked) come from our proxy, so they no longer read as a mirror refusing
the query. Credit the fix in CHANGELOG and note the mapping in CURRENT-STATE.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 22:36:53 -07:00
Sameh KhamisandClaude Opus 5.5 5472e54f73 Merge main into pr-665
Resolve the CHANGELOG conflict by keeping both sides' entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 22:36:35 -07:00
Sameh KhamisandClaude Opus 5.5 714d3dd1bb Merge main into pr-594
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 22:23:42 -07:00
Sameh KhamisandClaude Opus 5.5 9b1c75e539 Merge main into pr-688
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 22:17:12 -07:00
Sameh KhamisandClaude Opus 5.5 20a03aad87 fix(render): release the atmosphere probe's WebGL context
Lose the throwaway WebGL2 probe context right after the link probe instead
of leaving it for GC; browsers (Safari especially) cap live contexts. Note
that affected devices also lose globe-basemap fog, credit the changelog
entry and move it to the top.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 21:53:06 -07:00
Sameh KhamisandClaude Opus 5.5 351b5fcaa4 Merge main into pr-705
Resolve the CHANGELOG conflict with Fire Perimeters (#737) by keeping both
entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-24 21:52:39 -07:00
manjunath22466 54735a9e6f fix: allocate share-link layer tokens without collisions 2026-09-24 16:25:55 +05:30
ulihhhhandClaude Opus 5.5 b27e8a35c8 Merge upstream/main into feature/catalonia-cctv-cameras
Keeps PR #595 current with #730-#743. Clean merge, no conflicts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 10:50:18 +02:00
513dc87a0e feat(hud): Cyber HUD theme with GPU contact sonar (#706)
Add the opt-in Cyber HUD layout with coordinated map and cockpit panel
styling, GPU-only contact sonar (rings, range, power, opacity, sector) with
native-rendering fallback, and set_cyber_sonar voice controls. Sonar visual
style inspired by kk376's tactical naval sonar (#214).

Co-authored-by: Sameh Khamis <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-23 20:31:56 -07:00
f913066cf2 feat(fires): MODIS in Active Fires and a Fire Perimeters layer (#737)
Adds MODIS NRT to Active Fires and a keyless Fire Perimeters layer (NIFC WFIGS) with
InciWeb incident links, served through a capped, cached /api/fire-perimeters proxy.

Co-authored-by: Gustavo Beneduzi <[email protected]>
Co-authored-by: James Cooke <[email protected]>
2026-09-23 21:21:52 -05:00