'use client'; import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Layers, Minus, Plus, Plane, AlertTriangle, Activity, Satellite, Cctv, ChevronDown, ChevronUp, Ship, Eye, Anchor, Settings, Sun, Moon, BookOpen, Radio, Play, Pause, Square, FastForward, Globe, Flame, Wifi, Server, Shield, Zap, ToggleLeft, ToggleRight, Palette, CloudLightning, Mountain, Wind, Fish, TrainFront, Search, Droplets, Radar, MapPin, Truck, } from 'lucide-react'; import RoadCorridorLayerControls from '@/components/RoadCorridorLayerControls'; import { API_BASE } from '@/lib/api'; import { useLiveUamapScraperOptIn } from '@/hooks/useLiveUamapScraperOptIn'; import ConfirmDialog from '@/components/ui/ConfirmDialog'; import { onTileLoadingChange, resetTileLoading } from '@/lib/sentinelHub'; import packageJson from '../../package.json'; import { useTheme } from '@/lib/ThemeContext'; import { useTranslation } from '@/i18n'; import SarModeChooserModal from './SarModeChooserModal'; import KiwiSdrConsentDialog from './ui/KiwiSdrConsentDialog'; function relativeTime(iso: string | undefined): string { if (!iso) return ''; const diff = Date.now() - new Date(iso + 'Z').getTime(); if (diff < 0) return 'now'; const sec = Math.floor(diff / 1000); if (sec < 60) return `${sec}s ago`; const min = Math.floor(sec / 60); if (min < 60) return `${min}m ago`; const hr = Math.floor(min / 60); if (hr < 24) return `${hr}h ago`; return `${Math.floor(hr / 24)}d ago`; } // Map layer IDs to freshness keys from the backend source_timestamps dict const FRESHNESS_MAP: Record = { flights: 'commercial_flights', private: 'private_flights', jets: 'private_jets', military: 'military_flights', tracked: 'military_flights', earthquakes: 'earthquakes', satellites: 'satellites', ships_military: 'ships', ships_cargo: 'ships', ships_civilian: 'ships', ships_passenger: 'ships', ships_tracked_yachts: 'ships', ukraine_frontline: 'frontlines', global_incidents: 'gdelt', cctv: 'cctv', gps_jamming: 'commercial_flights', kiwisdr: 'kiwisdr', psk_reporter: 'psk_reporter', satnogs: 'satnogs_stations', tinygs: 'tinygs_satellites', firms: 'firms_fires', internet_outages: 'internet_outages', datacenters: 'datacenters', power_plants: 'power_plants', sigint_meshtastic: 'sigint', sigint_aprs: 'sigint', ukraine_alerts: 'ukraine_alerts', weather_alerts: 'weather_alerts', air_quality: 'air_quality', volcanoes: 'volcanoes', fishing_activity: 'fishing_activity', shodan_overlay: '', correlations: 'correlations', contradictions: 'correlations', uap_sightings: 'uap_sightings', wastewater: 'wastewater', ai_intel: '', crowdthreat: 'crowdthreat', road_corridor_trends: 'road_corridor_trends', }; // POTUS fleet ICAO hex codes for client-side filtering const POTUS_ICAOS: Record = { ADFDF8: { label: 'Air Force One (82-8000)', type: 'AF1' }, ADFDF9: { label: 'Air Force One (92-9000)', type: 'AF1' }, ADFEB7: { label: 'Air Force Two (98-0001)', type: 'AF2' }, ADFEB8: { label: 'Air Force Two (98-0002)', type: 'AF2' }, ADFEB9: { label: 'Air Force Two (99-0003)', type: 'AF2' }, ADFEBA: { label: 'Air Force Two (99-0004)', type: 'AF2' }, AE4AE6: { label: 'Air Force Two (09-0015)', type: 'AF2' }, AE4AE8: { label: 'Air Force Two (09-0016)', type: 'AF2' }, AE4AEA: { label: 'Air Force Two (09-0017)', type: 'AF2' }, AE4AEC: { label: 'Air Force Two (19-0018)', type: 'AF2' }, AE0865: { label: 'Marine One (VH-3D)', type: 'M1' }, AE5E76: { label: 'Marine One (VH-92A)', type: 'M1' }, AE5E77: { label: 'Marine One (VH-92A)', type: 'M1' }, AE5E79: { label: 'Marine One (VH-92A)', type: 'M1' }, }; import type { ActiveLayers, SelectedEntity, KiwiSDR, Scanner, TrackedFlight, } from '@/types/dashboard'; import { useDataSnapshot } from '@/hooks/useDataStore'; // --------------------------------------------------------------------------- // ScannerTracker — in-app audio player for tracked police scanner systems // --------------------------------------------------------------------------- function ScannerTracker({ scanner, onRelease, onFlyTo, }: { scanner: Scanner; onRelease: () => void; onFlyTo: () => void; }) { const audioRef = useRef(null); const recentPlayedRef = useRef>(new Set()); const fetchAndPlayRef = useRef<() => void>(() => undefined); const [isPlaying, setIsPlaying] = useState(false); const [isLoading, setIsLoading] = useState(false); const [playerMessage, setPlayerMessage] = useState('Ready to play the latest OpenMHz call.'); const [activeBurst, setActiveBurst] = useState<{ id: string; talkgroup: string; url: string; time?: string; len?: number; } | null>(null); const [volume, setVolume] = useState(0.8); const [isScanning, setIsScanning] = useState(false); const isScanningRef = useRef(false); const scanTimerRef = useRef | null>(null); // Cleanup on unmount useEffect(() => { return () => { isScanningRef.current = false; if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; } if (scanTimerRef.current) clearTimeout(scanTimerRef.current); }; }, []); // Sync volume useEffect(() => { if (audioRef.current) audioRef.current.volume = volume; }, [volume]); const scheduleScan = useCallback((delayMs = 3500) => { if (scanTimerRef.current) clearTimeout(scanTimerRef.current); scanTimerRef.current = setTimeout(() => { if (isScanningRef.current) { fetchAndPlayRef.current(); } }, delayMs); }, []); const fetchAndPlay = useCallback(async () => { if (!scanner.shortName) { setPlayerMessage('No OpenMHz system id is available for this scanner.'); return; } setIsLoading(true); setPlayerMessage('Checking recent calls...'); try { const res = await fetch(`${API_BASE}/api/radio/openmhz/calls/${scanner.shortName}`); if (!res.ok) { setPlayerMessage(`OpenMHz call lookup failed (${res.status}).`); return; } const calls = await res.json(); if (!calls?.length) { setPlayerMessage(isScanningRef.current ? 'No recent calls. Auto scan will retry.' : 'No recent calls for this system yet.'); if (isScanningRef.current) scheduleScan(8000); return; } const playable = calls.filter((call: { url?: string }) => Boolean(call?.url)); if (!playable.length) { setPlayerMessage('Recent calls did not include playable audio URLs.'); if (isScanningRef.current) scheduleScan(8000); return; } const pick = playable.find((call: { id?: string; _id?: string }) => { const id = String(call.id || call._id || ''); return id && !recentPlayedRef.current.has(id); }) || playable[0]; const burst = { id: pick.id || pick._id || String(Date.now()), talkgroup: String(pick.talkgroupNum || '???'), url: `${API_BASE}/api/radio/openmhz/audio?url=${encodeURIComponent(pick.url)}`, time: pick.time, len: Number(pick.len || 0), }; recentPlayedRef.current.add(String(burst.id)); if (recentPlayedRef.current.size > 40) { recentPlayedRef.current = new Set(Array.from(recentPlayedRef.current).slice(-20)); } setActiveBurst(burst); if (!audioRef.current) audioRef.current = new Audio(); audioRef.current.pause(); audioRef.current.src = burst.url; audioRef.current.volume = volume; audioRef.current.onended = () => { setIsPlaying(false); setPlayerMessage(isScanningRef.current ? 'Call ended. Scanning for the next call...' : 'Call ended.'); if (isScanningRef.current) scheduleScan(1200); }; audioRef.current.onerror = () => { setIsPlaying(false); setPlayerMessage('Audio failed to load. Trying another call shortly.'); if (isScanningRef.current) scheduleScan(2500); }; await audioRef.current.play(); setIsPlaying(true); setPlayerMessage(isScanningRef.current ? 'Playing. Auto scan is armed.' : 'Playing latest call.'); } catch (e) { console.error('Scanner audio error', e); setPlayerMessage('Audio playback failed. Try Auto Scan or another scanner.'); if (isScanningRef.current) scheduleScan(5000); } finally { setIsLoading(false); } }, [scanner.shortName, scheduleScan, volume]); fetchAndPlayRef.current = () => { void fetchAndPlay(); }; const stop = () => { if (scanTimerRef.current) { clearTimeout(scanTimerRef.current); scanTimerRef.current = null; } if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; } setIsPlaying(false); setIsLoading(false); setActiveBurst(null); setPlayerMessage('Stopped.'); if (isScanning) { setIsScanning(false); isScanningRef.current = false; } }; const toggleScan = () => { if (isScanning) { stop(); return; } setIsScanning(true); isScanningRef.current = true; void fetchAndPlay(); }; return (
SCANNER TRACKER {isPlaying && ( LIVE )} {isLoading && ( TUNING )}
{/* System info */}
{(scanner.name || 'UNKNOWN SYSTEM').toUpperCase()} {[scanner.city, scanner.state].filter(Boolean).join(', ')} {scanner.clientCount > 0 && · {scanner.clientCount} listeners} {activeBurst && ( TALKGROUP: {activeBurst.talkgroup} {activeBurst.len ? {activeBurst.len}s : null} )}
{/* Audio controls */}
setVolume(parseFloat(e.target.value))} className="w-16 accent-red-500" title="Volume" />
{playerMessage}
{/* Waveform visualizer */}
{Array.from({ length: 36 }).map((_, i) => ( ))}
{/* Action buttons */}
); } // --------------------------------------------------------------------------- // SdrTracker — in-app KiwiSDR receiver for tracked SDR stations // Opens a compact popup window (KiwiSDR uses HTTP + WebSockets so iframes // are blocked by mixed-content policies on HTTPS pages). // --------------------------------------------------------------------------- function SdrTracker({ sdr, onRelease, onFlyTo, }: { sdr: KiwiSDR; onRelease: () => void; onFlyTo: () => void; }) { const [isListening, setIsListening] = useState(false); const [consentDialogOpen, setConsentDialogOpen] = useState(false); const [consentDialogMode, setConsentDialogMode] = useState<'consent' | 'edit'>('consent'); const [currentCallsign, setCurrentCallsign] = useState(''); const popupRef = useRef(null); // Poll to detect when user closes the popup useEffect(() => { if (!isListening || !popupRef.current) return; const timer = setInterval(() => { if (popupRef.current?.closed) { setIsListening(false); popupRef.current = null; } }, 1000); return () => clearInterval(timer); }, [isListening]); // Close popup on unmount / release useEffect(() => { return () => { popupRef.current?.close(); }; }, []); // Load persisted callsign on mount useEffect(() => { if (typeof window === 'undefined') return; setCurrentCallsign((localStorage.getItem('kiwisdr_callsign') || '').trim()); }, []); const launchPopup = (callsign: string) => { if (!sdr.url) return; const tuneUrl = callsign ? `${sdr.url}${sdr.url.includes('?') ? '&' : '?'}n=${encodeURIComponent(callsign)}` : sdr.url; popupRef.current = window.open( tuneUrl, 'kiwisdr_receiver', 'width=800,height=600,menubar=no,toolbar=no,location=no,status=no', ); setIsListening(true); }; const openReceiver = () => { if (popupRef.current && !popupRef.current.closed) { popupRef.current.focus(); return; } if (!sdr.url) return; if (typeof window === 'undefined') return; const consented = localStorage.getItem('kiwisdr_consent_v1') === '1'; if (!consented) { setConsentDialogMode('consent'); setConsentDialogOpen(true); return; } const callsign = (localStorage.getItem('kiwisdr_callsign') || '').trim(); launchPopup(callsign); }; const handleConsentConfirm = (callsign: string) => { if (typeof window !== 'undefined') { localStorage.setItem('kiwisdr_consent_v1', '1'); if (callsign) { localStorage.setItem('kiwisdr_callsign', callsign); } else { localStorage.removeItem('kiwisdr_callsign'); } } setCurrentCallsign(callsign); setConsentDialogOpen(false); if (consentDialogMode === 'consent') { launchPopup(callsign); } }; const closeReceiver = () => { popupRef.current?.close(); popupRef.current = null; setIsListening(false); }; return (
SDR TRACKER {isListening && ( LIVE )}
{/* System info */}
{(sdr.name || 'REMOTE RECEIVER').toUpperCase()} {sdr.location && {sdr.location} · } {sdr.antenna && {sdr.antenna.slice(0, 40)}} {sdr.bands && ( {(Number(sdr.bands.split('-')[0]) / 1e6).toFixed(0)}- {(Number(sdr.bands.split('-')[1]) / 1e6).toFixed(0)} MHz {sdr.users !== undefined && ` · ${sdr.users}/${sdr.users_max || '?'} users`} )}
{/* Waveform visualizer — shows when receiver is open */} {isListening && (
{Array.from({ length: 36 }).map((_, i) => ( ))}
)} {/* Action buttons */}
{sdr.url && ( )}
{/* Callsign line with edit affordance */}
CALLSIGN:{' '} {currentCallsign || '(anonymous — KiwiSDR will prompt)'}
setConsentDialogOpen(false)} />
); } const WorldviewLeftPanel = React.memo(function WorldviewLeftPanel({ activeLayers, setActiveLayers, onSettingsClick, onLegendClick, gibsDate, setGibsDate, gibsOpacity, setGibsOpacity, onEntityClick, onFlyTo, trackedSdr, setTrackedSdr, trackedScanner, setTrackedScanner, shodanResultCount = 0, sentinelDate, setSentinelDate, sentinelOpacity, setSentinelOpacity, sentinelPreset, setSentinelPreset, isMinimized: isMinimizedProp, onMinimizedChange, onOpenSarAoiEditor, viewBoundsRef, }: { activeLayers: ActiveLayers; setActiveLayers: React.Dispatch>; onSettingsClick?: () => void; onLegendClick?: () => void; gibsDate?: string; setGibsDate?: (d: string) => void; gibsOpacity?: number; setGibsOpacity?: (o: number) => void; onEntityClick?: (entity: SelectedEntity) => void; onFlyTo?: (lat: number, lng: number) => void; trackedSdr?: KiwiSDR | null; setTrackedSdr?: (sdr: KiwiSDR | null) => void; trackedScanner?: Scanner | null; setTrackedScanner?: (s: Scanner | null) => void; shodanResultCount?: number; sentinelDate?: string; setSentinelDate?: (d: string) => void; sentinelOpacity?: number; setSentinelOpacity?: (o: number) => void; sentinelPreset?: string; setSentinelPreset?: (p: string) => void; isMinimized?: boolean; onMinimizedChange?: (minimized: boolean) => void; onOpenSarAoiEditor?: () => void; viewBoundsRef?: React.RefObject<{ south: number; west: number; north: number; east: number } | null>; }) { const data = useDataSnapshot() as import('@/types/dashboard').DashboardData; const { t } = useTranslation(); const [internalMinimized, setInternalMinimized] = useState(true); const isMinimized = isMinimizedProp !== undefined ? isMinimizedProp : internalMinimized; const setIsMinimized = (val: boolean | ((prev: boolean) => boolean)) => { const newVal = typeof val === 'function' ? val(isMinimized) : val; setInternalMinimized(newVal); onMinimizedChange?.(newVal); }; const { theme, toggleTheme, hudColor, cycleHudColor } = useTheme(); const [gibsPlaying, setGibsPlaying] = useState(false); const [potusEnabled, setPotusEnabled] = useState(true); const gibsIntervalRef = useRef | null>(null); // SAR mode chooser — prompts the first time the user enables the SAR // layer, remembers the choice, and auto-detects server-side Mode B. const [sarChoice, setSarChoice] = useState(() => { try { const stored = localStorage.getItem('shadowbroker_sar_mode_choice'); if (stored === 'a_only' || stored === 'b_active') return stored; } catch { // localStorage unavailable } return null; }); const [sarModalOpen, setSarModalOpen] = useState(false); const [sarPendingEnable, setSarPendingEnable] = useState(false); const [liveuamapModalOpen, setLiveuamapModalOpen] = useState(false); const [liveuamapPendingEnable, setLiveuamapPendingEnable] = useState<(() => void) | null>(null); const { needsConsentBeforeEnable, confirmOptIn } = useLiveUamapScraperOptIn(); const withGlobalIncidentsConsent = useCallback( (layerId: string, turningOn: boolean, apply: () => void) => { if (needsConsentBeforeEnable(layerId, turningOn)) { setLiveuamapPendingEnable(() => apply); setLiveuamapModalOpen(true); return; } apply(); }, [needsConsentBeforeEnable], ); // Auto-detect: if the backend already has Mode B creds configured // (via env or a previous runtime save), promote the stored choice to // 'b_active' without prompting. If it flips back to off, reset so the // next toggle re-prompts. useEffect(() => { let cancelled = false; const check = async () => { try { const res = await fetch(`${API_BASE}/api/sar/status`, { credentials: 'include', }); if (!res.ok || cancelled) return; const body = await res.json(); const modeBOn = Boolean(body?.products?.enabled); if (cancelled) return; if (modeBOn && sarChoice !== 'b_active') { try { localStorage.setItem('shadowbroker_sar_mode_choice', 'b_active'); } catch { // ignore } setSarChoice('b_active'); } else if (!modeBOn && sarChoice === 'b_active') { try { localStorage.removeItem('shadowbroker_sar_mode_choice'); } catch { // ignore } setSarChoice(null); } } catch { // network error — keep the current choice } }; check(); return () => { cancelled = true; }; // Run on mount only — the auto-detect is best-effort. // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Sentinel tile loading feedback const [sentinelInflight, setSentinelInflight] = useState(0); const [sentinelLoaded, setSentinelLoaded] = useState(0); useEffect(() => { const unsub = onTileLoadingChange((inflight, loaded) => { setSentinelInflight(inflight); setSentinelLoaded(loaded); }); return unsub; }, []); // Reset counters when sentinel layer is toggled off or settings change useEffect(() => { if (activeLayers.sentinel_hub) { resetTileLoading(); } }, [activeLayers.sentinel_hub, sentinelPreset, sentinelDate]); // GIBS time slider play/pause animation useEffect(() => { if (!gibsPlaying || !setGibsDate) { if (gibsIntervalRef.current) clearInterval(gibsIntervalRef.current); gibsIntervalRef.current = null; return; } gibsIntervalRef.current = setInterval(() => { if (!gibsDate) return; const d = new Date(gibsDate + 'T00:00:00'); d.setDate(d.getDate() + 1); const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); if (d > yesterday) { const start = new Date(); start.setDate(start.getDate() - 30); setGibsDate(start.toISOString().slice(0, 10)); } else { setGibsDate(d.toISOString().slice(0, 10)); } }, 1500); return () => { if (gibsIntervalRef.current) clearInterval(gibsIntervalRef.current); }; }, [gibsPlaying, gibsDate, setGibsDate]); // Compute ship category counts (memoized — ships array can be 1000+ items) const { militaryShipCount, cargoShipCount, passengerShipCount, civilianShipCount, trackedYachtCount, } = useMemo(() => { const ships = data?.ships; if (!ships || !ships.length) return { militaryShipCount: 0, cargoShipCount: 0, passengerShipCount: 0, civilianShipCount: 0, trackedYachtCount: 0, }; let military = 0, cargo = 0, passenger = 0, civilian = 0, trackedYacht = 0; for (const s of ships) { if (s.yacht_alert) { trackedYacht++; continue; } const t = s.type; if (t === 'carrier' || t === 'military_vessel') military++; else if (t === 'tanker' || t === 'cargo') cargo++; else if (t === 'passenger') passenger++; else civilian++; } return { militaryShipCount: military, cargoShipCount: cargo, passengerShipCount: passenger, civilianShipCount: civilian, trackedYachtCount: trackedYacht, }; }, [data?.ships]); // Compute SIGINT source counts const { meshtasticCount, aprsCount } = useMemo(() => { const totals = data?.sigint_totals; if (totals) { return { meshtasticCount: Number(totals.meshtastic || 0), aprsCount: Number(totals.aprs || 0) + Number(totals.js8call || 0), }; } const sigs = data?.sigint; if (!sigs || !sigs.length) return { meshtasticCount: 0, aprsCount: 0 }; let mesh = 0, aprs = 0; for (const s of sigs) { if (s.source === 'meshtastic') mesh++; else aprs++; } return { meshtasticCount: mesh, aprsCount: aprs }; }, [data?.sigint, data?.sigint_totals]); const cctvCount = Number(data?.cctv_total || data?.cctv?.length || 0); const satnogsCount = Number(data?.satnogs_total || data?.satnogs_stations?.length || 0); const tinygsCount = Number(data?.tinygs_total || data?.tinygs_satellites?.length || 0); // Find POTUS fleet planes currently airborne from tracked flights const potusFlights = useMemo(() => { const tracked = data?.tracked_flights; if (!tracked) return []; const results: { index: number; flight: TrackedFlight; meta: { label: string; type: string }; }[] = []; for (let i = 0; i < tracked.length; i++) { const f = tracked[i]; const icao = (f.icao24 || '').toUpperCase(); if (POTUS_ICAOS[icao]) { results.push({ index: i, flight: f, meta: POTUS_ICAOS[icao] }); } } return results; }, [data?.tracked_flights]); const sections = [ { label: t('layers.aircraft').toUpperCase(), icon: Plane, layers: [ { id: 'flights', name: t('layers.commercialFlights'), source: 'adsb.lol', count: data?.commercial_flights?.length || 0, icon: Plane, }, { id: 'private', name: t('layers.privateAircraft'), source: 'adsb.lol', count: data?.private_flights?.length || 0, icon: Plane, }, { id: 'jets', name: t('layers.privateJets'), source: 'adsb.lol', count: data?.private_jets?.length || 0, icon: Plane, }, { id: 'military', name: t('layers.militaryFlights'), source: 'adsb.lol', count: data?.military_flights?.length || 0, icon: AlertTriangle, }, { id: 'tracked', name: t('layers.trackedAircraft'), source: 'Plane-Alert DB', count: data?.tracked_flights?.length || 0, icon: Eye, }, { id: 'gps_jamming', name: t('layers.gpsJamming'), source: 'ADS-B NACp', count: data?.gps_jamming?.length || 0, icon: Radio, }, ], }, { label: t('layers.maritime').toUpperCase(), icon: Ship, layers: [ { id: 'ships_military', name: t('layers.militaryVessels'), source: 'AIS Stream', count: militaryShipCount, icon: Ship, }, { id: 'ships_cargo', name: t('layers.cargoShips'), source: 'AIS Stream', count: cargoShipCount, icon: Ship, }, { id: 'ships_civilian', name: t('layers.civilianShips'), source: 'AIS Stream', count: civilianShipCount, icon: Anchor, }, { id: 'ships_passenger', name: t('layers.passengerShips'), source: 'AIS Stream', count: passengerShipCount, icon: Anchor, }, { id: 'ships_tracked_yachts', name: t('layers.trackedYachts'), source: 'Yacht-Alert DB', count: trackedYachtCount, icon: Eye, }, { id: 'fishing_activity', name: t('layers.fishingActivity'), source: 'Global Fishing Watch', count: data?.fishing_activity?.length || 0, icon: Fish, }, ], }, { label: t('layers.space').toUpperCase(), icon: Satellite, layers: [ { id: 'satellites', name: t('layers.satellites'), source: (data?.satellite_source === 'celestrak' ? 'CelesTrak SGP4' : data?.satellite_source === 'tle_api' ? 'TLE API · SGP4' : data?.satellite_source === 'disk_cache' ? 'Cached · SGP4 (est.)' : 'CelesTrak SGP4') + (data?.satellite_analysis?.starlink?.total ? ` · ${data.satellite_analysis.starlink.total.toLocaleString()} Starlink` : '') + (data?.satellite_analysis?.maneuvers?.length ? ` · ${data.satellite_analysis.maneuvers.length} maneuver${data.satellite_analysis.maneuvers.length > 1 ? 's' : ''}` : ''), count: data?.satellites?.length || 0, icon: Satellite, }, { id: 'gibs_imagery', name: t('layers.gibsImagery'), source: 'NASA GIBS', count: null, icon: Globe, }, { id: 'highres_satellite', name: t('layers.highresSatellite'), source: 'Esri World Imagery', count: null, icon: Satellite, }, { id: 'sentinel_hub', name: t('layers.sentinelHub'), source: 'Copernicus CDSE', count: null, icon: Satellite, }, { id: 'viirs_nightlights', name: t('layers.viirsNightlights'), source: 'NASA GIBS', count: null, icon: Moon, }, { id: 'road_corridor_trends', name: t('layers.roadCorridorTrends'), source: t('layers.roadCorridorSource'), count: data?.road_corridor_trends?.corridors?.filter((c) => (c.total_detections ?? 0) > 0) .length ?? 0, icon: Truck, }, ], }, { label: t('layers.hazards').toUpperCase(), icon: AlertTriangle, layers: [ { id: 'earthquakes', name: t('layers.earthquakes'), source: 'USGS', count: data?.earthquakes?.length || 0, icon: Activity, }, { id: 'firms', name: t('layers.fires'), source: 'NASA FIRMS VIIRS', count: data?.firms_fires?.length || 0, icon: Flame, }, { id: 'ukraine_alerts', name: t('layers.ukraineAlerts'), source: 'alerts.in.ua', count: data?.ukraine_alerts?.length || 0, icon: AlertTriangle, }, { id: 'weather_alerts', name: t('layers.weatherAlerts'), source: 'NOAA/NWS', count: data?.weather_alerts?.length || 0, icon: CloudLightning, }, { id: 'volcanoes', name: t('layers.volcanoes'), source: 'Smithsonian GVP', count: data?.volcanoes?.length || 0, icon: Mountain, }, { id: 'air_quality', name: t('layers.airQuality'), source: 'OpenAQ', count: data?.air_quality?.length || 0, icon: Wind, }, { id: 'sar', name: t('layers.sar'), source: (data?.sar_anomalies?.length ? `OPERA/EGMS · ${data.sar_anomalies.length} alerts · ${data.sar_scenes?.length || 0} passes` : (data?.sar_scenes?.length ? `Catalog only · ${data.sar_scenes.length} Sentinel-1 passes · Alerts: sign up →` : 'Catalog only (free) · Alerts: sign up →')), count: data?.sar_anomalies?.length || 0, icon: Radar, }, ], }, { label: t('layers.uapSightings').toUpperCase(), icon: Eye, layers: [ { id: 'uap_sightings', name: t('layers.uapSightings'), source: 'NUFORC', count: data?.uap_sightings?.length || 0, icon: Eye, }, ], }, { label: t('layers.biosurveillance').toUpperCase(), icon: Droplets, layers: [ { id: 'wastewater', name: t('layers.wastewater'), source: 'WastewaterSCAN', count: data?.wastewater?.length || 0, icon: Droplets, }, ], }, { label: t('layers.infrastructure').toUpperCase(), icon: Server, layers: [ { id: 'cctv', name: t('layers.cctv'), source: 'CCTV Mesh + Street View', count: cctvCount, icon: Cctv, }, { id: 'datacenters', name: t('layers.datacenters'), source: 'DC Map (GitHub)', count: data?.datacenters?.length || 0, icon: Server, }, { id: 'internet_outages', name: t('layers.internetOutages'), source: 'IODA + RIPE Atlas', count: data?.internet_outages?.length || 0, icon: Wifi, }, { id: 'power_plants', name: t('layers.powerPlants'), source: 'WRI (Static)', count: data?.power_plants?.length || 0, icon: Zap, }, { id: 'military_bases', name: t('layers.militaryBases'), source: 'OSINT (Static)', count: data?.military_bases?.length || 0, icon: Shield, }, { id: 'trains', name: t('layers.trains'), source: 'Amtraker + DigiTraffic', count: data?.trains?.length || 0, icon: TrainFront, }, ], }, { label: t('layers.shodanOverlay').toUpperCase(), icon: Search, layers: [ { id: 'shodan_overlay', name: t('layers.shodanOverlay'), source: 'Operator Search', count: shodanResultCount, icon: Search, }, ], }, { label: t('layers.sigint').toUpperCase(), icon: Radio, layers: [ { id: 'kiwisdr', name: t('layers.kiwisdr'), source: 'KiwiSDR.com', count: data?.kiwisdr?.length || 0, icon: Radio, }, { id: 'psk_reporter', name: t('layers.pskReporter'), source: 'PSK Reporter', count: data?.psk_reporter?.length || 0, icon: Radio, }, { id: 'satnogs', name: t('layers.satnogs'), source: 'SatNOGS', count: satnogsCount, icon: Satellite, }, { id: 'tinygs', name: t('layers.tinygs'), source: 'TinyGS', count: tinygsCount, icon: Satellite, }, { id: 'scanners', name: t('layers.scanners'), source: 'OpenMHZ', count: data?.scanners?.length || 0, icon: Radio, }, { id: 'sigint_meshtastic', name: t('layers.meshtastic'), source: 'LoRa MQTT', count: meshtasticCount, icon: Radio, }, { id: 'sigint_aprs', name: t('layers.aprs'), source: 'APRS-IS / JS8', count: aprsCount, icon: Radio, }, ], }, { label: t('layers.overlays').toUpperCase(), icon: Globe, layers: [ { id: 'ukraine_frontline', name: t('layers.ukraineFrontline'), source: 'DeepStateMap', count: data?.frontlines ? 1 : 0, icon: AlertTriangle, }, { id: 'global_incidents', name: t('layers.globalIncidents'), source: 'GDELT', count: data?.gdelt?.length || 0, icon: Activity, }, { id: 'crowdthreat', name: t('layers.crowdThreat'), source: 'CrowdThreat', count: data?.crowdthreat?.length || 0, icon: Shield, }, { id: 'correlations', name: t('layers.correlations'), source: 'Cross-Layer Analysis', count: data?.correlations?.length || 0, icon: Zap, }, { id: 'contradictions', name: t('layers.contradictions'), source: 'Narrative Intelligence', count: data?.correlations?.filter((c: { type: string }) => c.type === 'contradiction').length || 0, icon: Zap, }, { id: 'day_night', name: t('layers.dayNight'), source: 'Solar Calc', count: null, icon: Sun, }, { id: 'ai_intel', name: t('layers.aiIntel'), source: 'OpenClaw AI', count: null, icon: Zap, }, ], }, ]; const [expandedSections, setExpandedSections] = useState>(() => { const initial: Record = {}; sections.forEach((s) => { initial[s.label] = false; }); return initial; }); const shipIcon = ( ); return ( <> {/* Header */}
TOP SECRET // SI-TK // NOFORN · KH11-4094 OPS-4168

FLIR

{onSettingsClick && ( )} {onLegendClick && ( )} v{packageJson.version}
{/* Data Layers Box */}
{/* Header / Toggle */}
setIsMinimized(!isMinimized)} >
DATA LAYERS
{isMinimized ? ( ) : ( )}
{!isMinimized && (
{/* SDR TRACKER — pinned to TOP when active, with embedded receiver */} {trackedSdr && ( setTrackedSdr?.(null)} onFlyTo={() => onFlyTo?.(trackedSdr.lat, trackedSdr.lon)} /> )} {/* SCANNER TRACKER — pinned when active, with in-app audio player */} {trackedScanner && ( setTrackedScanner?.(null)} onFlyTo={() => onFlyTo?.(trackedScanner.lat, trackedScanner.lng)} /> )} {/* POTUS Fleet — pinned to TOP when aircraft are active */} {potusEnabled && potusFlights.length > 0 && (
POTUS FLEET {potusFlights.length} ACTIVE
{potusFlights.map((pf) => { const color = pf.meta.type === 'AF1' ? '#ff1493' : pf.meta.type === 'M1' ? '#ff1493' : '#3b82f6'; const alt = pf.flight.alt || 0; const speed = pf.flight.speed_knots || 0; return (
{ if (onFlyTo && pf.flight.lat != null && pf.flight.lng != null) { onFlyTo(pf.flight.lat, pf.flight.lng); } if (onEntityClick) { onEntityClick({ type: 'tracked_flight', id: pf.flight.icao24 }); } }} >
{pf.meta.label} {alt > 0 ? `${Math.round(alt).toLocaleString()} ft` : 'GND'} ·{' '} {speed > 0 ? `${Math.round(speed)} kts` : 'STATIC'}
TRACK
); })}
)} {sections.map((section) => { const SectionIcon = section.icon; const sectionLayerIds = section.layers.map((l) => l.id); const allOn = sectionLayerIds.every( (id) => activeLayers[id as keyof typeof activeLayers], ); const anyOn = sectionLayerIds.some( (id) => activeLayers[id as keyof typeof activeLayers], ); const expanded = expandedSections[section.label] ?? true; const totalCount = section.layers.reduce( (sum, l) => sum + ((l.count as number) || 0), 0, ); return (
{/* Section header */}
setExpandedSections((prev) => ({ ...prev, [section.label]: !expanded })) } > {section.label} {anyOn && totalCount > 0 && ( {totalCount.toLocaleString()} )} {expanded ? ( ) : ( )}
{/* Section layers (collapsible) */} {expanded && (
{section.layers.map((layer) => { const Icon = layer.icon; const active = activeLayers[layer.id as keyof typeof activeLayers] || false; return (
{ // SAR first-run interception: if the user // is turning the SAR layer ON for the first // time and hasn't picked a mode yet, show // the chooser instead of flipping silently. if ( layer.id === 'sar' && !active && sarChoice === null ) { setSarPendingEnable(true); setSarModalOpen(true); return; } withGlobalIncidentsConsent(layer.id, !active, () => { setActiveLayers((prev: ActiveLayers) => ({ ...prev, [layer.id]: !active, })); }); }} >
{layer.id.startsWith('ships_') ? ( shipIcon ) : ( )}
{layer.name} {layer.id === 'shodan_overlay' ? layer.source : ( <> {layer.source} ·{' '} {active ? (() => { const fKey = FRESHNESS_MAP[layer.id]; const freshness = fKey && data?.freshness?.[fKey]; const rt = freshness ? relativeTime(freshness) : ''; return rt ? ( {rt} ) : ( 'LIVE' ); })() : 'OFF'} )}
{active && (layer.count ?? 0) > 0 && ( {(layer.count ?? 0).toLocaleString()} )} {layer.id !== 'shodan_overlay' && (
{active ? layer.id === 'sentinel_hub' ? 'SCAN' : 'ON' : 'OFF'}
)}
{/* GIBS Imagery inline controls */} {active && layer.id === 'gibs_imagery' && gibsDate && setGibsDate && setGibsOpacity && (
e.stopPropagation()} >
{ const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const selected = new Date(gibsDate + 'T00:00:00'); const diff = Math.round( (yesterday.getTime() - selected.getTime()) / 86400000, ); return 29 - Math.max(0, Math.min(29, diff)); })()} onChange={(e) => { const daysAgo = 29 - parseInt(e.target.value); const d = new Date(); d.setDate(d.getDate() - 1 - daysAgo); setGibsDate(d.toISOString().slice(0, 10)); }} className="flex-1 h-1 accent-cyan-500 cursor-pointer" />
{gibsDate}
OPC setGibsOpacity(parseInt(e.target.value) / 100) } className="w-16 h-1 accent-cyan-500 cursor-pointer" />
)} {/* SAR inline controls — AOI editor button */} {active && layer.id === 'road_corridor_trends' && ( )} {active && layer.id === 'sar' && onOpenSarAoiEditor && (
e.stopPropagation()} >
)} {/* Sentinel Hub inline controls */} {active && layer.id === 'sentinel_hub' && sentinelDate && setSentinelDate && setSentinelOpacity && setSentinelPreset && (
e.stopPropagation()} > {/* Preset selector + loading indicator */}
{sentinelInflight > 0 ? ( {sentinelInflight} tile{sentinelInflight !== 1 ? 's' : ''}… ) : sentinelLoaded > 0 ? ( {sentinelLoaded} loaded ) : null}
{/* Date slider — 0-29 days back */}
{ const today = new Date(); const selected = new Date(sentinelDate + 'T00:00:00'); const diff = Math.round( (today.getTime() - selected.getTime()) / 86400000, ); return 29 - Math.max(0, Math.min(29, diff)); })()} onChange={(e) => { const daysAgo = 29 - parseInt(e.target.value); const d = new Date(); d.setDate(d.getDate() - daysAgo); setSentinelDate(d.toISOString().slice(0, 10)); }} className="flex-1 h-1 accent-purple-500 cursor-pointer" />
{sentinelDate}
OPC setSentinelOpacity(parseInt(e.target.value) / 100) } className="w-16 h-1 accent-purple-500 cursor-pointer" />
)}
); })}
)}
); })} {/* POTUS Fleet — bottom section when inactive or hidden */} {(potusFlights.length === 0 || !potusEnabled) && (
POTUS FLEET
{!potusEnabled ? ( ) : ( NO ACTIVE AIRCRAFT )}
)}
)}
{sarModalOpen && ( { setSarModalOpen(false); setSarPendingEnable(false); }} onChoiceMade={(choice) => { setSarChoice(choice); if (sarPendingEnable) { setActiveLayers((prev: ActiveLayers) => ({ ...prev, sar: true })); setSarPendingEnable(false); } }} /> )} { setLiveuamapModalOpen(false); setLiveuamapPendingEnable(null); }} onConfirm={() => { void (async () => { try { await confirmOptIn(); liveuamapPendingEnable?.(); } catch (e) { console.warn('LiveUAMap opt-in failed:', e); } finally { setLiveuamapModalOpen(false); setLiveuamapPendingEnable(null); } })(); }} /> ); }); export default WorldviewLeftPanel;