Infer country and military force (PLA, JSDF, ROK, ROC) from ICAO hex
address blocks when the flag field is Unknown. Extract and extend aircraft
model classification to cover East Asian fighters, cargo, recon, and
tanker types with hyphen-normalized matching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 18 US military bases (Japan, Guam, South Korea, Hawaii, Diego Garcia)
as a toggleable map layer. Follows the existing data center layer pattern:
static JSON → backend fetcher → slow-tier API → frontend GeoJSON layer.
Includes red circle markers with labels, click popups showing operator
and branch info, and a toggle in the left panel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-update POST goes through Next.js proxy which dies when extracted
files trigger hot-reload. Network drops now transition to restart polling
instead of showing failure. Also adds admin key header and FastAPI error
field fallback. Gitignore updated to protect internal docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 03162f8a4b7ad8a0f2983f81361df7dba42a8689
selectedEntity.id was stored as a numeric array index into data.gdelt[]
and data.news[]. After any data refresh those arrays rebuild, so the
stored index pointed to a different item — showing wrong popup content.
GDELT features now use g.properties?.name || String(g.geometry.coordinates)
as a stable id; popups resolve via find(). News popups resolve via find()
matching alertKey. ThreatMarkers emits alertKey string instead of originalIdx.
ThreatMarkerProps updated: id: number → id: string | number.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: c2bfd0897a9ebd27e7c905ea3ac848a89883f140
interpFlight, interpShip, and interpSat were plain arrow functions
recreated on every render. Because interpTick fires every second,
TrackedFlightLabels received a new function reference every second
(preventing memo bailout) and all downstream useMemos closed over
these functions re-executed unnecessarily.
Wrap all three in useCallback([dtSeconds]) — dtSeconds is their
only reactive closure variable; interpolatePosition is a stable
module-level import and does not need to be listed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: 84c3c06407afa5c0227ac1b682cca1157498d1a5
- Remove WorldviewRightPanel from left HUD (declutter)
- Restore sliding sidebar animation via motion.div on both HUD containers
- Left tab (LAYERS): springs to x:-360 when hidden, tab tracks edge
- Right tab (INTEL): springs to x:+360 when hidden, tab tracks edge
- Both use spring animation (damping:30 stiffness:250)
- ChevronLeft/Right icons flip direction with open state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: 5a573165d27db1704f513ce9fd503ddc3f6892ef
Removes WorldviewRightPanel render and import from page.tsx.
The effects state is preserved as it continues to feed MaplibreViewer.
Left HUD column now contains only the data layers panel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: 0cdb2a60bd8436b7226866e2f4086496beed1587
Adds subtle amber glow circles behind both cluster and individual
tower markers for a pulsing radar-station effect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: bf6cee0f3b468006356fd95dcf83a27d5e62e5f6
Replaced the circle cluster layer with a symbol layer using the same
radio tower icon. Clusters show the tower with a count label below.
No more orange blobs at any zoom level.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 0b1cb0d2a082dde4dcefe12518cdfb28b492ab89
Individual nodes now render as amber radio tower SVGs with signal waves.
Clusters use a subtle amber glow ring with count label instead of solid
orange blobs. Much less visual clutter against the flight/ship markers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 96baa3415440118a6084c739d500a1ce5951d27f
- kiwisdr_fetcher.py imported non-existent `smart_request` (renamed to
`fetch_with_curl`), causing silent ImportError → 0 nodes returned
- Replaced KiwiSDR iframe embed with clean "OPEN SDR RECEIVER" button.
The full KiwiSDR web UI (waterfall, frequency controls, callsign
prompt) is unusable at 288px — better opened in a new tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: aa0fcd92b2390d6a8943b68f2f7eb9b900c7bbb7
Replaces array-index-based selection with stable backend identifiers so
selected entities persist correctly across data refreshes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: 14e316d055ba0b1fe16a2be301fcaaf4349b5a29
Each alert bubble now has an × button in the top-right corner.
Clicking it hides the alert for the session and clears its selection
if it was active.
- Dismissal keyed by stable content hash (title+coords) so dismissed
state survives data.news array replacement on every 60s polling cycle
- Button stopPropagation prevents accidental entity selection on dismiss
- Single useState<Set<string>> — avoids naming collision with the
react-map-gl `Map` import that caused the previous black-screen crash
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: ce2dec52a9a40a581995323354414b278abdf443
The catch-all route.ts that proxies frontend /api/* requests to the backend
was accidentally deleted during the v0.8.0 rebase against PR #44. Without it,
all API fetches return 404 and nothing loads on the map.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 811ec765320d9813efc654fee53ef0e5d5fecc78
New features:
- POTUS fleet (AF1, AF2, Marine One) with hot-pink icons + gold halo ring
- 9-color aircraft system: military, medical, police, VIP, privacy, dictators
- Sentinel-2 fullscreen overlay with download/copy/open buttons (green themed)
- Carrier homeport deconfliction — distinct pier positions instead of stacking
- Toggle all data layers button (cyan when active, excludes MODIS Terra)
- Version badge + update checker + Discussions shortcut in UI
- Overhauled MapLegend with POTUS fleet, wildfires, infrastructure sections
- Data center map layer with ~700 global DCs from curated dataset
Fixes:
- All Air Force Two ICAO hex codes now correctly identified
- POTUS icon priority over grounded state
- Sentinel-2 no longer overlaps bottom coordinate bar
- Region dossier Nominatim 429 rate-limit retry/backoff
- Docker ENV legacy format warnings resolved
- UI buttons cyan in dark mode, grey in light mode
- Circuit breaker for flaky upstream APIs
Community: @suranyami — parallel multi-arch Docker builds + runtime BACKEND_URL fix (PR #35, #44)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 7c523df70a2d26f675603166e3513d29230592cd
Two bugs introduced by the Next.js proxy Route Handler:
1. ERR_CONTENT_DECODING_FAILED — Node.js fetch() automatically
decompresses gzip/br responses from the backend, but the proxy was
still forwarding Content-Encoding and Content-Length headers to the
browser. The browser would then try to decompress already-decompressed
data and fail. Fixed by stripping Content-Encoding and Content-Length
from upstream response headers.
2. BACKEND_URL shell env leak into Docker Compose — docker-compose.yml
used ${BACKEND_URL:-http://backend:8000}, which was being overridden
by BACKEND_URL=http://localhost:8000 set in .mise.local.toml for local
dev. Inside the frontend container, localhost:8000 does not exist,
causing all proxied requests to return 502. Fixed by hardcoding
http://backend:8000 in docker-compose.yml so the shell environment
cannot override it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: 036c62d2c0
Previously, NEXT_PUBLIC_API_URL was a build-time Next.js variable, making
it impossible to configure the backend URL in docker-compose `environment`
without rebuilding the image.
This change introduces a proper server-side proxy:
- next.config.ts: adds a rewrite rule that forwards all /api/* requests
to BACKEND_URL (read at server startup, not baked at build time).
Defaults to http://localhost:8000 so local dev works without config.
- api.ts: API_BASE is now an empty string — all fetch calls use relative
/api/... paths, which the Next.js server proxies to the backend.
- docker-compose.yml: replaces NEXT_PUBLIC_API_URL build arg with a
runtime BACKEND_URL env var defaulting to http://backend:8000, using
Docker's internal networking. Port 8000 no longer needs to be exposed.
- README: updates Docker setup docs, standalone compose example, and
environment variable reference to reflect BACKEND_URL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: a3b18e23c1
Previously, NEXT_PUBLIC_API_URL was a build-time Next.js variable, making
it impossible to configure the backend URL in docker-compose `environment`
without rebuilding the image.
This change introduces a proper server-side proxy:
- next.config.ts: adds a rewrite rule that forwards all /api/* requests
to BACKEND_URL (read at server startup, not baked at build time).
Defaults to http://localhost:8000 so local dev works without config.
- api.ts: API_BASE is now an empty string — all fetch calls use relative
/api/... paths, which the Next.js server proxies to the backend.
- docker-compose.yml: replaces NEXT_PUBLIC_API_URL build arg with a
runtime BACKEND_URL env var defaulting to http://backend:8000, using
Docker's internal networking. Port 8000 no longer needs to be exposed.
- README: updates Docker setup docs, standalone compose example, and
environment variable reference to reflect BACKEND_URL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: b4c9e78cdd
New features:
- Custom RSS Feed Manager: add/remove/prioritize up to 20 news sources
from the Settings panel with weight levels 1-5. Persists across restarts.
- Global Data Center Map Layer: 2,000+ DCs plotted worldwide with clustering,
server-rack icons, and automatic internet outage cross-referencing.
- Imperative map rendering: high-volume layers bypass React reconciliation
via direct setData() calls with debounced updates on dense layers.
- Enhanced /api/health with per-source freshness timestamps and counts.
Fixes:
- Data center coordinates fixed for 187 Southern Hemisphere entries
- Docker CORS_ORIGINS passthrough in docker-compose.yml
- Start scripts warn on Python 3.13+ compatibility
- Settings panel redesigned with tabbed UI (API Keys / News Feeds)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 950c308f04
New intelligence layers:
- NASA FIRMS VIIRS fire hotspots (5K+ global thermal anomalies, flame icons)
- NOAA space weather badge (Kp index in status bar)
- IODA regional internet outage monitoring (grey markers, BGP/ping only)
Key improvements:
- Fire clusters use flame-shaped icons (not circles) for clear differentiation
- Internet outages are region-level with reliable datasources only
- Removed radiation layer (no viable free real-time API)
- All outage markers grey to avoid color confusion with other layers
- Filtered out merit-nt telescope data that produced misleading percentages
Updated changelog modal, README, and package.json for v0.5.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 195c6b64b9
Add 4 new intelligence layers for v0.5:
- NASA FIRMS VIIRS thermal anomaly tiles (frontend-only WMTS)
- NOAA Space Weather Kp index badge in bottom bar
- Safecast radiation monitoring with clustered markers
- IODA internet outage alerts at country centroids
All use free keyless APIs. All layers default to off.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: 7cb926e227