diff --git a/static/css/modes/drone.css b/static/css/modes/drone.css index 1e57e71..7941125 100644 --- a/static/css/modes/drone.css +++ b/static/css/modes/drone.css @@ -1,5 +1,80 @@ /* Drone Intelligence Styles */ +/* ── Main visuals panel ── */ +.drone-visuals-container { + display: none; + flex-direction: column; + width: 100%; + height: 100%; + overflow: hidden; + background: var(--bg-primary); +} + +.drone-visuals-header { + flex-shrink: 0; + padding: 10px 16px; + border-bottom: 1px solid var(--border-color); + background: var(--bg-secondary); +} + +.drone-visuals-stats { + display: flex; + gap: 24px; +} + +.drone-vsstat { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; +} + +.drone-vsstat-value { + font-size: 22px; + font-weight: 700; + font-family: var(--font-mono); + color: var(--text-primary); + line-height: 1; +} + +.drone-vsstat-label { + font-size: 9px; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-dim); +} + +.drone-visuals-body { + flex: 1; + min-height: 0; + display: flex; + overflow: hidden; +} + +.drone-contact-panel { + width: 300px; + flex-shrink: 0; + overflow-y: auto; + border-right: 1px solid var(--border-color); + padding: 10px; + display: flex; + flex-direction: column; + gap: 6px; +} + +.drone-main-map { + flex: 1; + min-width: 0; +} + +.drone-empty-state { + padding: 24px 12px; + text-align: center; + font-size: 11px; + color: var(--text-dim); + line-height: 1.6; +} + .drone-vector-pills { display: flex; flex-wrap: wrap; @@ -69,12 +144,6 @@ color: var(--accent-red); } -.drone-map { - height: 280px; - border-radius: 4px; - border: 1px solid var(--border-color); - margin: 0 12px 12px; -} .drone-marker-high-risk { animation: dsc-distress-pulse 1.5s infinite; diff --git a/static/js/modes/drone.js b/static/js/modes/drone.js index dbc3fee..3c39e92 100644 --- a/static/js/modes/drone.js +++ b/static/js/modes/drone.js @@ -17,9 +17,9 @@ function _initMap() { if (_map) return; - const mapEl = document.getElementById('droneMap'); + const mapEl = document.getElementById('droneMainMap'); if (!mapEl || typeof L === 'undefined') return; - _map = L.map('droneMap', { zoomControl: true }).setView([20, 0], 2); + _map = L.map('droneMainMap', { zoomControl: true }).setView([20, 0], 2); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 18, @@ -64,7 +64,9 @@ function _upsertCard(contact) { const listEl = document.getElementById('droneContactList'); + const emptyEl = document.getElementById('droneContactEmpty'); if (!listEl) return; + if (emptyEl) emptyEl.style.display = 'none'; let card = document.getElementById('drone-card-' + contact.id); if (!card) { card = document.createElement('div'); @@ -80,8 +82,8 @@ const vectors = (contact.detection_vectors || []).map(function (v) { return '' + v + ''; }).join(''); - const alt = contact.altitude_m != null ? contact.altitude_m.toFixed(0) + 'm' : '—'; - const spd = contact.speed_ms != null ? contact.speed_ms.toFixed(1) + 'm/s' : '—'; + const alt = contact.altitude_m != null ? contact.altitude_m.toFixed(0) + ' m' : '—'; + const spd = contact.speed_ms != null ? contact.speed_ms.toFixed(1) + ' m/s' : '—'; card.innerHTML = [ '
', ' ' + (contact.serial_number || contact.id) + '', @@ -138,10 +140,13 @@ .then(function (r) { return r.json(); }) .then(function (contacts) { const nonCompliant = contacts.filter(function (c) { return !c.compliant; }).length; - const countEl = document.getElementById('droneContactCount'); - const ncEl = document.getElementById('droneNonCompliantCount'); - if (countEl) countEl.textContent = contacts.length; - if (ncEl) ncEl.textContent = nonCompliant; + const highRisk = contacts.filter(function (c) { return c.risk_level === 'high'; }).length; + const set = function (id, val) { const el = document.getElementById(id); if (el) el.textContent = val; }; + set('droneContactCount', contacts.length); + set('droneNonCompliantCount', nonCompliant); + set('droneVsContacts', contacts.length); + set('droneVsNonCompliant', nonCompliant); + set('droneVsHighRisk', highRisk); }) .catch(function () {}); } @@ -201,5 +206,9 @@ }); } - window.DroneMode = { init: init, destroy: destroy }; + function invalidateMap() { + if (_map) _map.invalidateSize(); + } + + window.DroneMode = { init: init, destroy: destroy, invalidateMap: invalidateMap }; })(); diff --git a/templates/index.html b/templates/index.html index a2c862b..a283dcb 100644 --- a/templates/index.html +++ b/templates/index.html @@ -2212,6 +2212,35 @@
+ + +
-

WiFi Interface (monitor mode)

- +

WiFi Interface

+
+ + +
-

RTL-SDR Device (433 MHz)

- -
- -
- +

SDR Settings

+
+ + +
+
+ +
@@ -51,11 +56,4 @@ Non-compliant: 0

- -
-

Detected Contacts

-
-
- -