diff --git a/setup.sh b/setup.sh index b28a3d2..ad0d59a 100755 --- a/setup.sh +++ b/setup.sh @@ -226,7 +226,7 @@ check_tools() { check_optional "hackrf_sweep" "HackRF spectrum analyzer" hackrf_sweep check_required "dump1090" "ADS-B decoder" dump1090 check_required "acarsdec" "ACARS decoder" acarsdec - check_optional "dumpvdl2" "VDL2 decoder" dumpvdl2 + check_required "dumpvdl2" "VDL2 decoder" dumpvdl2 check_required "AIS-catcher" "AIS vessel decoder" AIS-catcher aiscatcher check_optional "satdump" "Weather satellite decoder (NOAA/Meteor)" satdump echo @@ -949,9 +949,9 @@ install_macos_packages() { ok "acarsdec already installed" fi - progress "Installing dumpvdl2 (optional)" + progress "Installing dumpvdl2" if ! cmd_exists dumpvdl2; then - install_dumpvdl2_from_source_macos || warn "dumpvdl2 not available. VDL2 decoding will not be available." + install_dumpvdl2_from_source_macos || fail "dumpvdl2 installation failed. VDL2 decoding requires dumpvdl2." else ok "dumpvdl2 already installed" fi @@ -1472,9 +1472,9 @@ install_debian_packages() { fi cmd_exists acarsdec || install_acarsdec_from_source_debian - progress "Installing dumpvdl2 (optional)" + progress "Installing dumpvdl2" if ! cmd_exists dumpvdl2; then - install_dumpvdl2_from_source_debian || warn "dumpvdl2 not available. VDL2 decoding will not be available." + install_dumpvdl2_from_source_debian || fail "dumpvdl2 installation failed. VDL2 decoding requires dumpvdl2." else ok "dumpvdl2 already installed" fi diff --git a/static/css/adsb_dashboard.css b/static/css/adsb_dashboard.css index c53eb6d..409fd3c 100644 --- a/static/css/adsb_dashboard.css +++ b/static/css/adsb_dashboard.css @@ -31,8 +31,11 @@ body { font-family: var(--font-sans); background: var(--bg-dark); color: var(--text-primary); - min-height: 100vh; - overflow-x: hidden; + height: 100dvh; + height: 100vh; /* Fallback */ + display: flex; + flex-direction: column; + overflow: hidden; } /* Animated radar sweep background */ @@ -227,16 +230,14 @@ body { } /* Main dashboard grid - Mobile first */ -/* Header ~52px + Nav 44px + Stats strip ~55px = ~151px, using 160px for safety */ .dashboard { position: relative; z-index: 10; display: flex; flex-direction: column; gap: 0; - height: calc(100dvh - 160px); - height: calc(100vh - 160px); /* Fallback */ - min-height: 400px; + flex: 1; + min-height: 0; } /* Tablet: Two-column layout */ @@ -249,13 +250,29 @@ body { } } -/* Desktop: Full layout with ACARS */ +/* Desktop: Full layout with ACARS/VDL2 + map + sidebar */ @media (min-width: 1024px) { .dashboard { grid-template-columns: auto 1fr 300px; } } +/* Left sidebars wrapper (ACARS + VDL2) */ +.left-sidebars { + display: none; +} + +@media (min-width: 1024px) { + .left-sidebars { + display: flex; + flex-direction: row; + grid-column: 1; + grid-row: 1; + height: 100%; + overflow: hidden; + } +} + /* ACARS sidebar (left of map) - Collapsible */ .acars-sidebar { display: none; @@ -267,12 +284,10 @@ body { min-height: 0; } -/* Show ACARS sidebar on desktop */ -@media (min-width: 1024px) { - .acars-sidebar { - display: flex; - max-height: calc(100dvh - 160px); - } +/* Show ACARS sidebar inside wrapper */ +.left-sidebars .acars-sidebar { + display: flex; + height: 100%; } .acars-collapse-btn { @@ -430,11 +445,10 @@ body { min-height: 0; } -@media (min-width: 1024px) { - .vdl2-sidebar { - display: flex; - max-height: calc(100dvh - 160px); - } +/* Show VDL2 sidebar inside wrapper */ +.left-sidebars .vdl2-sidebar { + display: flex; + height: 100%; } .vdl2-collapse-btn { @@ -652,6 +666,8 @@ body { position: relative; flex: 1; min-height: 300px; + min-width: 0; + overflow: hidden; } @media (min-width: 768px) { @@ -1453,7 +1469,7 @@ body { display: flex !important; flex-direction: column !important; height: auto !important; - min-height: calc(100dvh - 160px); + min-height: 400px; overflow-y: auto !important; overflow-x: hidden; -webkit-overflow-scrolling: touch; @@ -1654,7 +1670,7 @@ body { } .strip-stat.source-stat .strip-value { - font-size: 11px; + font-size: 14px; } .strip-stat.session-stat { diff --git a/static/js/core/agents.js b/static/js/core/agents.js index 72515a2..b0f4694 100644 --- a/static/js/core/agents.js +++ b/static/js/core/agents.js @@ -423,7 +423,7 @@ async function syncAgentModeStates(agentId) { }); // Also check modes that might need to be marked as stopped - const allModes = ['sensor', 'pager', 'adsb', 'wifi', 'bluetooth', 'ais', 'dsc', 'acars', 'aprs', 'rtlamr', 'tscm', 'satellite', 'listening_post']; + const allModes = ['sensor', 'pager', 'adsb', 'wifi', 'bluetooth', 'ais', 'dsc', 'acars', 'vdl2', 'aprs', 'rtlamr', 'tscm', 'satellite', 'listening_post']; allModes.forEach(mode => { if (!agentRunningModes.includes(mode)) { syncModeUI(mode, false, agentId); @@ -704,6 +704,7 @@ function syncModeUI(mode, isRunning, agentId = null) { 'wifi': 'setWiFiRunning', 'bluetooth': 'setBluetoothRunning', 'acars': 'setAcarsRunning', + 'vdl2': 'setVdl2Running', 'listening_post': 'setListeningPostRunning' }; @@ -865,12 +866,12 @@ function connectAgentStream(mode, onMessage) { } let streamUrl; - if (currentAgent === 'local') { - streamUrl = `/${mode}/stream`; - } else { - // For remote agents, proxy SSE through controller - streamUrl = `/controller/agents/${currentAgent}/${mode}/stream`; - } + if (currentAgent === 'local') { + streamUrl = `/${mode}/stream`; + } else { + // For remote agents, proxy SSE through controller + streamUrl = `/controller/agents/${currentAgent}/${mode}/stream`; + } agentEventSource = new EventSource(streamUrl); @@ -878,7 +879,7 @@ function connectAgentStream(mode, onMessage) { try { const data = JSON.parse(event.data); - onMessage(data); + onMessage(data); } catch (e) { console.error('Error parsing SSE message:', e); } diff --git a/templates/adsb_dashboard.html b/templates/adsb_dashboard.html index 8e473b8..d685d70 100644 --- a/templates/adsb_dashboard.html +++ b/templates/adsb_dashboard.html @@ -139,6 +139,8 @@
+ +