Commit Graph
2904 Commits
Author SHA1 Message Date
Sameh Khamis 6fd072bf79 Merge pull request #1086 from mvanhorn/fix/drone-airspace
feat(layers): add FAA drone restrictions airspace layer
2026-11-03 14:52:24 -08:00
Sameh Khamis 329ef7e01c test(drone-airspace): check the share token in CURRENT-STATE, not the changelog fragment
npm run changelog consumes changelog.d fragments at release, so a test that reads the fragment would fail with ENOENT afterwards. CURRENT-STATE is permanent.
2026-11-03 14:45:05 -08:00
Sameh Khamis 266825eddb fix(drone-airspace): show FAA times-of-use line breaks as separators
FAA marks line breaks in TIMESOFUSE with a literal %, which 56 live
restricted-area cards showed verbatim ("SAT - SUN; %OTHER TIMES BY NOTAM").
Read each marker as a '; ' separator, absorbing a semicolon already before
it.
2026-11-03 14:36:57 -08:00
Sameh Khamis add905e6b9 docs(drone-airspace): correct the share token, voice support and coverage
The changelog fragment named share-link token 6 (sea marks); the layer
reserves 8. CURRENT-STATE said voice could toggle and query the layer and
listed it among the analyst record sources, but it is in VOICE_OFF_LAYERS.
Say voice is off for now, and that the layer covers FAA prohibited and
restricted special-use airspace only, not the UAS Facility Map / LAANC grids
or TFRs. A test pins these claims to the token ledger and the voice manifest.
2026-11-03 14:35:59 -08:00
Sameh Khamis 585392cbcf fix(drone-airspace): refuse cross-site requests to the FAA proxy
A foreign Origin or Sec-Fetch-Site: cross-site request was answered and
counted against the client's 60-a-minute quota, and could start a refresh.
Run the keyless same-site checks the other map routes use before rate
limiting and cache acquisition; reverse-proxy forwarding headers stay
accepted. List the route with the keyless map routes in SECURITY.md.
2026-11-03 14:35:14 -08:00
Sameh Khamis ca17412c70 fix(drone-airspace): redraw a zone when only its boundary changes
The refresh signature left out the polygons, and these rows carry no
timestamp that moves with the geometry, so an amended boundary with the same
attributes kept the old shape and card anchor until the layer was destroyed.
Add a deterministic hash of each zone's rings to the signature.
2026-11-03 14:34:28 -08:00
Sameh Khamis 8e63420f06 fix(drone-airspace): show stale FAA snapshots as stale and cap them at a day
The source dropped the proxy's stale flag and fetch time, so the layer row
read nominal and freshly updated through an outage, and the proxy served its
last snapshot indefinitely. Carry stale and fetchedAt through the source as
Fire Perimeters does, date a stale snapshot by its fetch, and stop serving a
snapshot more than 24 hours old.
2026-11-03 14:34:01 -08:00
Sameh Khamis 6bdedd8523 fix(drone-airspace): refuse empty or incompletely paged FAA snapshots
An upstream answer whose zones all fail validation normalized to an empty
list, and paging that stopped at the page limit while the service still
flagged more records was accepted as complete. Both were cached as a fresh
snapshot for an hour and replaced the last good one. Treat them as failed
refreshes so stale-on-error keeps serving the previous snapshot.
2026-11-03 14:33:10 -08:00
Matt Van Horn 08b8be26fe Merge remote-tracking branch 'upstream/main' into fix/drone-airspace
# Conflicts:
#	docs/CURRENT-STATE.md
#	src/app/constructCatalog.test.mjs
#	src/app/sourceComposition.test.mjs
#	src/data/layerState.test.mjs
#	src/data/layerStateTokenLedger.test.mjs
#	src/i18n/locales/ar.json
2026-11-03 11:55:42 -08:00
Sameh Khamis 1facc09f8d Merge pull request #1519 from manjunath22466/manjunath/hal-769-gev-ux-improve-visibility-of-the-250-km-radius-circle
feat(awareness): add calm sonar pulses that flash the range ring and echo contacts
2026-11-03 11:13:29 -08:00
Sameh Khamis 0e3b1f67cf Merge branch 'main' into manjunath/hal-769-gev-ux-improve-visibility-of-the-250-km-radius-circle
# Conflicts:
#	scripts/format-scope.json
#	src/layers/awareness/policy.js
#	src/layers/awareness/rendering.js
2026-11-03 11:00:53 -08:00
Sameh Khamis a4e1763b53 Merge pull request #1518 from manjunath22466/manjunath/hal-769-range-ring-visibility
fix(awareness): make the 250 km range ring readable on any ground
2026-11-03 10:59:48 -08:00
Sameh Khamis 4fc889ad19 Merge branch 'main' into manjunath/hal-769-range-ring-visibility 2026-11-03 10:48:55 -08:00
German Khokhlov e29d3bd871 Merge pull request #1495 from quanticsoul4772/upstream/traffic-paired-flow-density
fix(traffic): give paired TomTom sides of one street a two-way share of dots
2026-11-03 22:48:27 +04:00
Sameh Khamis 02a6739466 fix(awareness): follow a gesture to its last contact, and drop it on blur
Lifting one finger of a pinch removed the document listeners, so the
remaining finger's drag, which Cesium carries on with, stopped counting.
A gesture now ends only when its last pointer lifts or no touches are
left. And a gesture whose end never reaches the page (the window loses
focus mid-drag, or the page is hidden) left the listeners attached, so
a later unrelated drag counted as camera input; a blur or hidden page
now stops following it and drops its pending input.
2026-11-03 10:47:23 -08:00
Sameh Khamis de634952ec fix(awareness): keep a long drag or touch counting as camera input
Only canvas events refreshed the 1.5 s input window, and a touch only on
touchstart. Cesium follows a drag that leaves the canvas at document
level (in Firefox, and wherever there are no pointer events), so a long
gesture kept moving the camera after the window closed, and the pulses
rested mid-gesture. From a pointer or touch start on the canvas until
its end or cancel, moves at document level now count too; the listeners
go when the gesture ends or the layer detaches.
2026-11-03 10:40:30 -08:00
Sameh Khamis 790b8fc860 Merge branch 'manjunath/hal-769-range-ring-visibility' into maintainer fixes 2026-11-03 10:39:45 -08:00
Sameh Khamis 56a7cacbec fix(awareness): ignore canvas input while the cockpit owns the camera
The cockpit turns Cesium's camera controls off and moves the camera on
its own, so a click, drag or wheel on the canvas moved nothing yet still
counted as the user handling the map: the cockpit's next move woke a
nine-second pulse burst. Input now counts only while the camera controls
take it, and a gesture still pending when another owner takes the
camera is dropped.
2026-11-03 10:39:09 -08:00
Sameh Khamis fe3d17df02 fix(awareness): give the pulse material a named type so Cesium caches it once
Cesium keeps every material type in a global cache for the page's
lifetime. The pulse disc's fabric had no type, so each new animated
visual (a subject switch, a reselect, reduced motion turned off) added a
GUID-named entry that was never removed. Name the type, and give each
material built from the cached template its own uniform objects back.
2026-11-03 10:39:06 -08:00
German Khokhlov e8735163c7 Merge branch 'main' into upstream/traffic-paired-flow-density 2026-11-03 22:38:31 +04:00
Manjunath 6cede0f2e2 Merge origin/main (acc9531b) into the range ring visibility branch
Brings #1518 up to date with main. The only conflict was
scripts/format-scope.json, where main restored a block of entries
(#1527) next to this branch's addition. Resolved as the union of both
sides: every entry from main and from this branch is kept, the
duplicate rateLimitKeys line is dropped, and every listed path exists.
Against main, this branch now adds only
src/layers/awareness/rangeRing.test.mjs to the list.
2026-11-04 00:07:11 +05:30
Sameh Khamis daba7f93a7 docs(awareness): state the range ring's measured ~50 m accuracy
The ring is up to about 49 m off the geodesic radius at 45 degrees, not
a few metres. Also attach the range ring style's JSDoc to its constant.
2026-11-03 10:34:19 -08:00
Sameh Khamis 39a697aa3c fix(awareness): keep the range ring a full circle at the poles
At or within about 1e-7 degrees of a pole the latitude/longitude
destination formula cancels out, and the 128-point ring collapsed to
three distinct points joined by chords of about 500 km. Step along the
great circle in 3D from the centre's up, north and east unit vectors
instead. Results at ordinary latitudes and across the antimeridian are
unchanged.
2026-11-03 10:34:10 -08:00
Sameh Khamis 0d32f59b40 Merge branch 'main' into manjunath/hal-769-range-ring-visibility
# Conflicts:
#	scripts/format-scope.json
2026-11-03 10:33:19 -08:00
Sameh Khamis acc9531b3f Merge pull request #846 from bilawalsidhu/feat/voice-geometry
feat(voice): voice geometry — reusable areas, drawn-area counts, recent imagery by voice
2026-11-03 09:58:58 -08:00
Sameh Khamis 553f0c06ee Merge branch 'main' into feat/voice-geometry 2026-11-03 09:49:42 -08:00
Sameh Khamis 48146f8e5b Merge pull request #1522 from bilawalsidhu/fix/zoom-out-runaway
fix: keep the globe on screen when zooming out from a tilted view
2026-11-03 09:42:53 -08:00
Sameh Khamis 4f95763a28 style: format the new area and place-search tests 2026-11-03 09:32:03 -08:00
Sameh Khamis f675938412 test(voice): record the measured prompt size, 55,210
The budget test measures the full instructions plus every tool. At this
head that is 55,210, ~290 under the 55,500 ceiling; the note said 55,370,
which that measurement does not produce.
2026-11-03 09:30:14 -08:00
Sameh Khamis 6b92602589 fix(osm): key place-search clients by IPv6 /64
/api/osm/features keyed its rate limit and its one-search-per-client slot
by the full peer address, so an IPv6 host could rotate addresses inside
its /64 to take both global slots and slip the 12/min limit. It now uses
clientNetworkKey, as /api/overpass does.
2026-11-03 09:29:52 -08:00
Sameh Khamis b531dd545b Merge branch 'feat/voice-geometry' into maintainer fixes
Picks up acd87e6c: the keyless cross-site gate on /api/osm/features
(582bfce6) and main at 93804d5d. SECURITY.md, the format-scope list and
the schema-test comments take the branch's side; the place-search budget
wording from the timeout fix is kept.
2026-11-03 09:29:23 -08:00
Sameh Khamis cc3d61a960 fix(camera): keep the zoom ceiling fixed while tracking a target
While the camera tracks an entity or looks at a point, Cesium measures
zoom distance from the target, not the Earth. Raising the ceiling to the
camera's altitude then lifted it with every outward step, so a tracked
camera could zoom out without limit. The guard now follows the altitude
only for an untracked 3D camera and otherwise keeps its 40,000 km
ceiling.
2026-11-03 09:29:22 -08:00
Manjunath acd87e6cf1 Merge branch 'main' into feat/voice-geometry
Picks up #1527, which restores the 97 format-scope entries an earlier merge
dropped; this branch had kept the same 97. The resolved list is main's plus
this PR's 12 test and helper files, 1,222 paths, all present.
2026-11-03 22:55:34 +05:30
Manjunath a851199f5b fix(awareness): rest the range pulses unless the user moves the camera
Review follow-ups for the sonar pulses.

Wake on user input only: a camera move wakes the pulses only within
1.5 s of a pointer, wheel or touch on the Cesium canvas, where its
camera controller listens. Tracking, the cockpit view, an orbit or a
fly-to move the camera on every frame and no longer keep the pulses
running. Keys don't count: the camera controller reads none.

Below the disc: with the camera under 2.5 km the pulses rest
completely, with no disc, ring flash, echoes or frame requests.

Echo brightness: echoes scale by the contact bracket's drawn alpha,
which includes the Cyber sonar sweep, rounded to the nearest band with
no floor, so an echo is never brighter than its bracket.

Disc follow: the pulse disc follows the subject by a model-matrix
rotation about the Earth's centre and is rebuilt only every 20 km
instead of every 500 m.

Tests: a cap on burst length; teardown through disable(); the echo's
spread and alpha on the canvas; echoes under the Cyber sonar never
brighter than their bracket; waking by pointer, wheel, touch and drag
but not by keys, app camera moves or stale input; the rest below the
disc; and tighter asserts. CURRENT-STATE notes that terrain above
2.5 km still cuts through the disc and ring.
2026-11-03 22:50:32 +05:30
Sameh Khamis 16b383ab6f fix(area): detach a lookup's abort listener once it settles
Each resolve() that raced the caller's signal added an abort listener and
never removed it, so a long-lived signal collected one listener per
completed lookup. The listener is now removed when the call settles and
is not added at all to a signal that is already aborted.
2026-11-03 09:16:52 -08:00
Sameh Khamis 3d2896cf27 fix(osm): default an absent /api/osm/features limit to 500
Without a limit parameter the route read null, Number(null) is 0, and the
clamp turned that into a one-feature list. An absent or empty limit now
gets the documented default of 500.
2026-11-03 09:13:40 -08:00
Sameh Khamis 68667e2e3c fix(area): accept "State of Texas" and "USA" as the units they name
resolve_area compared the spoken name with the unit's name literally, so
"State of Texas" (admin1) and "USA" or "UK" (country) were refused with
AREA_IDENTITY_MISMATCH although the bundled matcher found exactly those
units. Both identity checks, the bundled one and the outline ladder's, now
also accept the prefix forms and same-unit country aliases the matcher
reads, at the level they imply, so the level check still applies.
"Great Britain", "Britain", "America" and "Korea" still need their own
names, and the natural-region check stays literal.
2026-11-03 09:12:28 -08:00
Sameh Khamis 9e3786716d test(camera): pin that application controls install the zoom-out guard
The guard's own tests pass even if createApplicationControls stops
installing it. This checks the ceiling and frame listeners are in place
after construction and gone after teardown.
2026-11-03 09:11:51 -08:00
Sameh Khamis b817d5802b fix(camera): let a two-finger tilt win over finger-spacing drift
Above 300 km the guard treated any shrinking pinch distance as an outward
zoom, so a two-finger tilt whose fingers drifted slightly closer had its
pitch overwritten. A two-finger gesture now counts as a zoom only when the
spacing changes more than the fingers' midpoint moves.
2026-11-03 09:10:58 -08:00
Sameh Khamis e4b3933166 fix(camera): read zoom input at the ceiling when the scene is idle
The guard reads its input in preRender, which Cesium only fires on frames
that draw. In request-render mode, wheel input at the ceiling doesn't move
the camera, so nothing drew and the input was applied later by an
unrelated frame. The guard now requests a frame through the render
governor whenever it has unread input.
2026-11-03 09:10:30 -08:00
Sameh Khamis 08ff7f3e9d fix(camera): let a camera above the zoom ceiling zoom in without a jump
Cesium clamps any zoom that starts above maximumZoomDistance to that
limit, in either direction, so a share link or flight that left the
camera above 40,000 km jumped to the ceiling on the first wheel tick.
While the camera is above the ceiling, the guard now uses its current
height as the limit and lowers it as the camera descends.
2026-11-03 09:09:55 -08:00
Sameh Khamis 540629a0fc fix(osm): keep a capped place list incomplete in analyst follow-ups
osm_query said "At least N" when the server capped its list, but handed
the places to the analyst engine with truncated:false, so "which of those
have emergency?" answered as if the list were whole. The handoff now
carries the cap and its caveat, and follow-ups (and their follow-ups)
stay incomplete with an unknown total.
2026-11-03 09:09:28 -08:00
Sameh Khamis 4efba8ccca fix(osm): end the server's place search before the voice answer gives up
The voice answer waited 25 s for /api/osm/features, while the server let
Overpass run for 25 s and itself waited up to 40 s per endpoint and 45 s
in all. When the browser gave up first it closed the request, the route
cancelled the upstream query, and nothing was cached, so the OSM_TIMEOUT
retry started the same query from scratch and timed out again.

The server now tells Overpass 20 s, waits 22 s per endpoint and 22 s in
all, which ends inside the client's 25 s. A test pins that ordering.
2026-11-03 09:08:16 -08:00
Sameh Khamis 51637a5e96 Merge branch 'main' into feat/voice-geometry
# Conflicts:
#	SECURITY.md
#	scripts/format-scope.json
#	src/app/constructCatalog.test.mjs
#	src/app/sourceComposition.test.mjs
#	src/firstRunExperience.test.mjs
#	src/i18n/locales/ar.json
#	src/i18n/locales/de.json
#	src/i18n/locales/es.json
#	src/i18n/locales/fr.json
#	src/i18n/locales/it.json
#	src/i18n/locales/ja.json
#	src/i18n/locales/pt.json
#	src/i18n/locales/ru.json
#	src/i18n/locales/uk.json
#	src/i18n/locales/zh-CN.json
#	src/i18n/locales/zh-Hant.json
#	src/voice/actionSchemas.test.mjs
2026-11-03 09:07:16 -08:00
Sameh Khamis d5a568e55f Merge branch 'main' into fix/zoom-out-runaway 2026-11-03 09:04:51 -08:00
Sameh Khamis 93804d5dbd Merge pull request #1527 from bilawalsidhu/sameh/restore-format-scope
fix(tooling): restore 97 format-scope entries dropped by a merge
2026-11-03 09:04:26 -08:00
Sameh Khamis ceb3fe1b2b fix(tooling): restore 97 format-scope entries dropped by a merge
Merge commit 34906c9a resolved the end-of-list conflict in scripts/format-scope.json with the branch's side, which dropped the 97 entries main had appended since Oct 26. Those files all still exist; 64 of them are tests that runtime discovery does not cover, so format:check had stopped checking them.

Each entry goes back at its position on main before the merge. All 1680 files pass npm run format:check.
2026-11-03 08:55:02 -08:00
Manjunath 582bfce660 fix(osm): gate place search like /api/overpass after #1308
#1308 moved /api/overpass to the keyless cross-site gate: foreign origins
and cross-site fetches are still refused, but forwarding headers are not,
so the map works behind a reverse proxy. Voice place search spends the same
operator Overpass, so it now uses the same gate; with the stricter one,
place search failed behind a proxy while the map kept working. The prompt
lint note records the merged size, 55,370 with an operator Overpass.
2026-11-03 22:18:06 +05:30
Manjunath 1e12d5ba26 Merge branch 'main' into feat/voice-geometry
Picks up #1308 (Overpass admission), #114 (Sea Marks and Ski Pistes),
#1206 (radio country coverage) and #1435. Resolutions:
- SECURITY.md: main's rewritten cross-site paragraph, with voice place
  search listed beside /api/overpass among the keyless map routes.
- scripts/format-scope.json: both sides. A merge on main dropped 97 entries
  (being restored there); keeping them, this PR's 12 and main's 2 new ones
  keeps every file in format:check. All 1,222 paths exist.
- Translation catalogs: main's two new keys and this PR's 47, no overlap.
- Layer count 36 (33 + OSM Places + Sea Marks + Ski Pistes); both schema
  pins re-derived, the first-run length is exactly 30,919 + 292 + 107.
2026-11-03 22:16:52 +05:30
Said Turksever fa8db45d63 Merge commit '2ebde97fb3af19402eab83fca2bfbec9ddfd166a' into easy-wins-20261103-pr1518 2026-11-03 16:17:33 +00:00