Release v2.13.1 - Help modal and navigation improvements

- Add help modal system with keyboard shortcuts reference
- Add Main Dashboard button in navigation bar
- Make settings modal accessible from all dashboards
- Dashboard CSS improvements and consistency fixes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-02-04 20:05:07 +00:00
parent ebc5754684
commit 24332a4e23
20 changed files with 893 additions and 163 deletions

View File

@@ -22,6 +22,8 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/core/variables.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/help-modal.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/adsb_dashboard.css') }}">
<script>
window.INTERCEPT_SHARED_OBSERVER_LOCATION = {{ shared_observer_location | tojson }};
@@ -49,8 +51,6 @@
<input type="checkbox" id="showAllAgents" onchange="toggleShowAllAgents()"> All
</label>
</div>
<a href="#" onclick="history.back(); return false;" class="back-link">Back</a>
<a href="/?mode=aircraft" class="back-link">Main Dashboard</a>
</div>
</header>
@@ -2372,7 +2372,7 @@ sudo make install</code>
now.toISOString().substring(11, 19) + ' UTC';
}
function initMap() {
async function initMap() {
radarMap = L.map('radarMap', {
center: [observerLocation.lat, observerLocation.lon],
zoom: 7,
@@ -2382,7 +2382,9 @@ sudo make install</code>
// Use settings manager for tile layer (allows runtime changes)
window.radarMap = radarMap;
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
if (typeof Settings !== 'undefined') {
// Wait for settings to load from server before applying tiles
await Settings.init();
Settings.createTileLayer().addTo(radarMap);
Settings.registerMap(radarMap);
} else {
@@ -4521,6 +4523,14 @@ sudo make install</code>
}
</style>
<!-- Settings Modal -->
{% include 'partials/settings-modal.html' %}
<!-- Help Modal -->
{% include 'partials/help-modal.html' %}
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}"></script>
<!-- Agent Manager -->
<script src="{{ url_for('static', filename='js/core/agents.js') }}"></script>
<script src="{{ url_for('static', filename='js/core/global-nav.js') }}"></script>

View File

@@ -11,6 +11,8 @@
{% endif %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/help-modal.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/adsb_history.css') }}">
</head>
<body>
@@ -769,6 +771,14 @@
}
});
</script>
<!-- Settings Modal -->
{% include 'partials/settings-modal.html' %}
<!-- Help Modal -->
{% include 'partials/help-modal.html' %}
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}"></script>
<script src="{{ url_for('static', filename='js/core/global-nav.js') }}"></script>
</body>
</html>

View File

@@ -6,9 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>iNTERCEPT // Remote Agents</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/agents.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/core/variables.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/core/base.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/agents.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/help-modal.css') }}">
<style>
.agents-container {
max-width: 1200px;
@@ -224,26 +228,6 @@
opacity: 0.5;
}
/* Navigation links */
.nav-links {
display: flex;
gap: 16px;
margin-bottom: 20px;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--accent-cyan);
text-decoration: none;
font-size: 14px;
}
.back-link:hover {
text-decoration: underline;
}
/* Toast notifications */
.toast {
position: fixed;
@@ -304,22 +288,6 @@
{% include 'partials/nav.html' with context %}
<div class="agents-container">
<div class="nav-links">
<a href="#" onclick="history.back(); return false;" class="back-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
Back
</a>
<a href="/" class="back-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
Dashboard
</a>
</div>
<div class="agents-header">
<h1>Remote Agents</h1>
</div>
@@ -587,6 +555,14 @@
// Load agents on page load
document.addEventListener('DOMContentLoaded', loadAgents);
</script>
<!-- Settings Modal -->
{% include 'partials/settings-modal.html' %}
<!-- Help Modal -->
{% include 'partials/help-modal.html' %}
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}"></script>
<script src="{{ url_for('static', filename='js/core/global-nav.js') }}"></script>
</body>
</html>

View File

@@ -23,6 +23,8 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/ais_dashboard.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/help-modal.css') }}">
<script>
window.INTERCEPT_SHARED_OBSERVER_LOCATION = {{ shared_observer_location | tojson }};
</script>
@@ -49,8 +51,6 @@
<input type="checkbox" id="showAllAgents" onchange="toggleShowAllAgents()"> All
</label>
</div>
<a href="#" onclick="history.back(); return false;" class="back-link">Back</a>
<a href="/" class="back-link">Main Dashboard</a>
</div>
</header>
@@ -390,7 +390,7 @@
};
// Initialize map
function initMap() {
async function initMap() {
if (observerLocation) {
document.getElementById('obsLat').value = observerLocation.lat;
document.getElementById('obsLon').value = observerLocation.lon;
@@ -404,7 +404,9 @@
// Use settings manager for tile layer (allows runtime changes)
window.vesselMap = vesselMap;
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
if (typeof Settings !== 'undefined') {
// Wait for settings to load from server before applying tiles
await Settings.init();
Settings.createTileLayer().addTo(vesselMap);
Settings.registerMap(vesselMap);
} else {
@@ -1553,6 +1555,14 @@
}
</style>
<!-- Settings Modal -->
{% include 'partials/settings-modal.html' %}
<!-- Help Modal -->
{% include 'partials/help-modal.html' %}
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}"></script>
<!-- Agent Manager -->
<script src="{{ url_for('static', filename='js/core/agents.js') }}"></script>
<script src="{{ url_for('static', filename='js/core/global-nav.js') }}"></script>

View File

@@ -39,6 +39,7 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
{% endif %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/acars.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/aprs.css') }}">
@@ -2070,6 +2071,24 @@
}
}
function applySettingsFromQuery() {
const params = new URLSearchParams(window.location.search);
if (params.get('settings') === '1') {
// Remove settings param from URL to avoid reopening on refresh
params.delete('settings');
const newUrl = params.toString()
? window.location.pathname + '?' + params.toString()
: window.location.pathname;
window.history.replaceState({}, '', newUrl);
// Open settings modal after a brief delay to ensure page is ready
setTimeout(() => {
if (typeof showSettings === 'function') {
showSettings();
}
}, 100);
}
}
function acceptDisclaimer() {
localStorage.setItem('disclaimerAccepted', 'true');
document.getElementById('disclaimerModal').classList.add('disclaimer-hidden');
@@ -2410,6 +2429,9 @@
// Apply mode from URL query (e.g., /?mode=wifi)
applyModeFromQuery();
// Check for settings=1 query param (from dashboard settings button)
applySettingsFromQuery();
});
// Toggle section collapse
@@ -7923,7 +7945,7 @@
});
}
function initAprsMap() {
async function initAprsMap() {
if (aprsMap) return;
const mapContainer = document.getElementById('aprsMap');
@@ -7938,7 +7960,9 @@
window.aprsMap = aprsMap;
// Use settings manager for tile layer (allows runtime changes)
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
if (typeof Settings !== 'undefined') {
// Wait for settings to load from server before applying tiles
await Settings.init();
Settings.createTileLayer().addTo(aprsMap);
Settings.registerMap(aprsMap);
} else {
@@ -8846,7 +8870,7 @@
let observerMarker = null;
let satPositionInterval = null;
function initGroundTrackMap() {
async function initGroundTrackMap() {
const mapContainer = document.getElementById('groundTrackMap');
if (!mapContainer || groundTrackMap) return;
@@ -8859,7 +8883,9 @@
window.groundTrackMap = groundTrackMap;
// Use settings manager for tile layer (allows runtime changes)
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
if (typeof Settings !== 'undefined') {
// Wait for settings to load from server before applying tiles
await Settings.init();
Settings.createTileLayer().addTo(groundTrackMap);
Settings.registerMap(groundTrackMap);
} else {

View File

@@ -12,22 +12,25 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/agents.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/help-modal.css') }}">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--font-sans: 'Space Mono', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'Space Mono', 'Fira Code', 'Consolas', monospace;
--bg-primary: #0a0a0f;
--bg-secondary: #12121a;
--bg-tertiary: #1a1a2e;
--text-primary: #e0e0e0;
--text-secondary: #888;
--border-color: #2a2a3e;
--accent-cyan: #00d4ff;
--accent-green: #00ff88;
--accent-red: #ff3366;
--accent-orange: #ff9f1c;
--bg-primary: #0a0c10;
--bg-secondary: #0f1218;
--bg-tertiary: #151a23;
--text-primary: #e8eaed;
--text-secondary: #9ca3af;
--text-dim: #4b5563;
--border-color: #1f2937;
--accent-cyan: #4a9eff;
--accent-green: #22c55e;
--accent-red: #ef4444;
--accent-orange: #f59e0b;
--accent-purple: #a855f7;
}
@@ -120,12 +123,13 @@
font-family: var(--font-mono);
}
/* Header ~50px + Nav 44px + Status bar ~40px = ~134px, using 150px for safety */
.main-grid {
display: grid;
grid-template-columns: 1fr 300px;
gap: 20px;
padding: 20px;
height: calc(100vh - 110px);
height: calc(100vh - 150px);
}
.data-panel {
@@ -157,7 +161,7 @@
.panel-count {
font-size: 10px;
padding: 2px 8px;
background: rgba(0, 212, 255, 0.2);
background: rgba(74, 158, 255, 0.2);
color: var(--accent-cyan);
border-radius: 10px;
font-family: var(--font-mono);
@@ -188,7 +192,7 @@
}
.panel-tab.active {
background: rgba(0, 212, 255, 0.1);
background: rgba(74, 158, 255, 0.1);
color: var(--accent-cyan);
border-color: var(--accent-cyan);
}
@@ -226,7 +230,7 @@
}
.data-table tr:hover {
background: rgba(0, 212, 255, 0.05);
background: rgba(74, 158, 255, 0.05);
}
.mono {
@@ -245,7 +249,7 @@
gap: 4px;
padding: 2px 6px;
font-size: 9px;
background: rgba(0, 212, 255, 0.15);
background: rgba(74, 158, 255, 0.15);
color: var(--accent-cyan);
border-radius: 8px;
font-family: var(--font-mono);
@@ -260,7 +264,7 @@
/* Different colors for different agents */
.source-badge:nth-child(2) {
background: rgba(0, 255, 136, 0.15);
background: rgba(34, 197, 94, 0.15);
color: var(--accent-green);
}
.source-badge:nth-child(2) .dot {
@@ -276,7 +280,7 @@
}
.source-badge:nth-child(4) {
background: rgba(255, 159, 28, 0.15);
background: rgba(245, 158, 11, 0.15);
color: var(--accent-orange);
}
.source-badge:nth-child(4) .dot {
@@ -435,23 +439,23 @@
gap: 6px;
margin-top: 4px;
padding: 3px 8px;
background: rgba(0, 212, 255, 0.1);
background: rgba(74, 158, 255, 0.1);
border-radius: 4px;
font-size: 10px;
}
.location-estimate.high {
background: rgba(0, 255, 136, 0.15);
background: rgba(34, 197, 94, 0.15);
border-left: 2px solid var(--accent-green);
}
.location-estimate.medium {
background: rgba(255, 159, 28, 0.15);
background: rgba(245, 158, 11, 0.15);
border-left: 2px solid var(--accent-orange);
}
.location-estimate.low {
background: rgba(255, 51, 102, 0.15);
background: rgba(239, 68, 68, 0.15);
border-left: 2px solid var(--accent-red);
}
@@ -493,11 +497,11 @@
letter-spacing: 0.5px;
}
.type-badge.type-wifi { background: rgba(0, 212, 255, 0.2); color: var(--accent-cyan); }
.type-badge.type-wifi { background: rgba(74, 158, 255, 0.2); color: var(--accent-cyan); }
.type-badge.type-bluetooth { background: rgba(168, 85, 247, 0.2); color: var(--accent-purple); }
.type-badge.type-adsb { background: rgba(0, 255, 136, 0.2); color: var(--accent-green); }
.type-badge.type-ais { background: rgba(255, 159, 28, 0.2); color: var(--accent-orange); }
.type-badge.type-sensor { background: rgba(255, 51, 102, 0.2); color: var(--accent-red); }
.type-badge.type-adsb { background: rgba(34, 197, 94, 0.2); color: var(--accent-green); }
.type-badge.type-ais { background: rgba(245, 158, 11, 0.2); color: var(--accent-orange); }
.type-badge.type-sensor { background: rgba(239, 68, 68, 0.2); color: var(--accent-red); }
@media (max-width: 1024px) {
.main-grid {
@@ -1106,6 +1110,14 @@
connectStream();
addLogEntry('system', 'Network Monitor initialized');
</script>
<!-- Settings Modal -->
{% include 'partials/settings-modal.html' %}
<!-- Help Modal -->
{% include 'partials/help-modal.html' %}
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}"></script>
<script src="{{ url_for('static', filename='js/core/global-nav.js') }}"></script>
</body>
</html>

View File

@@ -0,0 +1,318 @@
{#
Help Modal Partial
Provides consistent help modal across all pages
#}
<!-- Help Modal -->
<div id="helpModal" class="help-modal" onclick="if(event.target === this) hideHelp()">
<div class="help-content">
<button class="help-close" onclick="hideHelp()">&times;</button>
<h2>iNTERCEPT Help</h2>
<div class="help-tabs">
<button class="help-tab active" data-tab="icons" onclick="switchHelpTab('icons')">Icons</button>
<button class="help-tab" data-tab="modes" onclick="switchHelpTab('modes')">Modes</button>
<button class="help-tab" data-tab="wifi" onclick="switchHelpTab('wifi')">WiFi</button>
<button class="help-tab" data-tab="tips" onclick="switchHelpTab('tips')">Tips</button>
</div>
<!-- Icons Section -->
<div id="help-icons" class="help-section active">
<h3>Stats Bar Icons</h3>
<div class="icon-grid">
<div class="icon-item"><span class="icon">&#128223;</span><span class="desc">POCSAG messages decoded</span></div>
<div class="icon-item"><span class="icon">&#128224;</span><span class="desc">FLEX messages decoded</span></div>
<div class="icon-item"><span class="icon">&#128232;</span><span class="desc">Total messages received</span></div>
<div class="icon-item"><span class="icon">&#127777;&#65039;</span><span class="desc">Unique sensors detected</span></div>
<div class="icon-item"><span class="icon">&#128202;</span><span class="desc">Device types found</span></div>
<div class="icon-item"><span class="icon">&#128752;&#65039;</span><span class="desc">Satellites monitored</span></div>
<div class="icon-item"><span class="icon">&#128225;</span><span class="desc">WiFi Access Points</span></div>
<div class="icon-item"><span class="icon">&#128100;</span><span class="desc">Connected WiFi clients</span></div>
<div class="icon-item"><span class="icon">&#129309;</span><span class="desc">Captured handshakes</span></div>
<div class="icon-item"><span class="icon">&#128641;</span><span class="desc">Detected drones (click for details)</span></div>
<div class="icon-item"><span class="icon">&#9888;&#65039;</span><span class="desc">Rogue APs (click for details)</span></div>
<div class="icon-item"><span class="icon">&#128309;</span><span class="desc">Bluetooth devices</span></div>
<div class="icon-item"><span class="icon">&#128205;</span><span class="desc">BLE beacons / APRS stations</span></div>
</div>
<h3>Mode Tab Icons</h3>
<div class="icon-grid">
<div class="icon-item"><span class="icon">&#128223;</span><span class="desc">Pager - POCSAG/FLEX decoder</span></div>
<div class="icon-item"><span class="icon">&#128225;</span><span class="desc">433MHz - Sensor decoder</span></div>
<div class="icon-item"><span class="icon">&#9889;</span><span class="desc">Meters - Utility meter decoder</span></div>
<div class="icon-item"><span class="icon">&#9992;&#65039;</span><span class="desc">Aircraft - ADS-B tracking &amp; history</span></div>
<div class="icon-item"><span class="icon">&#128674;</span><span class="desc">Vessels - AIS &amp; VHF DSC distress</span></div>
<div class="icon-item"><span class="icon">&#128251;</span><span class="desc">Spy Stations - Number stations database</span></div>
<div class="icon-item"><span class="icon">&#128205;</span><span class="desc">APRS - Amateur radio tracking</span></div>
<div class="icon-item"><span class="icon">&#128752;&#65039;</span><span class="desc">Satellite - Pass prediction</span></div>
<div class="icon-item"><span class="icon">&#128246;</span><span class="desc">WiFi - Network scanner</span></div>
<div class="icon-item"><span class="icon">&#128309;</span><span class="desc">Bluetooth - BT/BLE scanner</span></div>
<div class="icon-item"><span class="icon">&#128251;</span><span class="desc">Listening Post - SDR scanner</span></div>
<div class="icon-item"><span class="icon">&#128269;</span><span class="desc">TSCM - Counter-surveillance</span></div>
</div>
</div>
<!-- Modes Section -->
<div id="help-modes" class="help-section">
<h3>Pager Mode</h3>
<ul class="tip-list">
<li>Decodes POCSAG and FLEX pager signals using RTL-SDR</li>
<li>Set frequency to local pager frequencies (common: 152-158 MHz)</li>
<li>Messages are displayed in real-time as they're decoded</li>
<li>Use presets for common pager frequencies</li>
</ul>
<h3>433MHz Sensor Mode</h3>
<ul class="tip-list">
<li>Decodes wireless sensors on 433.92 MHz ISM band</li>
<li>Detects temperature, humidity, weather stations, tire pressure monitors</li>
<li>Supports many common protocols (Acurite, LaCrosse, Oregon Scientific, etc.)</li>
<li>Device intelligence builds profiles of recurring devices</li>
</ul>
<h3>Utility Meter Mode</h3>
<ul class="tip-list">
<li>Decodes utility meter transmissions (water, gas, electric) using rtlamr</li>
<li>Supports ERT protocol on 912 MHz (North America) or 868 MHz (Europe)</li>
<li>Displays meter IDs and consumption data in real-time</li>
<li>Supports SCM, SCM+, IDM, NetIDM, and R900 message types</li>
</ul>
<h3>Aircraft (Dashboard)</h3>
<ul class="tip-list">
<li>Opens the dedicated ADS-B Dashboard for aircraft tracking</li>
<li>Features radar scope, map view, airband audio, and ACARS decoding</li>
<li>Optional history mode persists data to Postgres for long-term analysis</li>
<li>Access history dashboard at <code>/adsb/history</code></li>
</ul>
<h3>Vessels (Dashboard)</h3>
<ul class="tip-list">
<li>Opens the AIS Dashboard for maritime vessel tracking</li>
<li>Displays vessel name, MMSI, callsign, destination, and navigation data</li>
<li><strong>VHF DSC Channel 70:</strong> Monitors maritime distress frequency (156.525 MHz)</li>
<li>Decodes DSC messages: Distress, Urgency, Safety, and Routine calls</li>
<li>MMSI country identification via Maritime Identification Digits (MID)</li>
<li>Visual alerts for DISTRESS and URGENCY messages with map markers</li>
</ul>
<h3>Spy Stations</h3>
<ul class="tip-list">
<li>Database of number stations and diplomatic HF networks</li>
<li>Browse stations from priyom.org with frequencies and schedules</li>
<li>Filter by type (number/diplomatic), country, and mode</li>
<li>Famous stations: UVB-76 "The Buzzer", Cuban HM01, Israeli E17z</li>
<li>Click "Tune" to listen via Listening Post mode</li>
</ul>
<h3>APRS Mode</h3>
<ul class="tip-list">
<li>Decodes APRS (Automatic Packet Reporting System) on VHF</li>
<li>Tracks amateur radio operators transmitting position data</li>
<li>Regional frequencies: 144.390 MHz (N. America), 144.800 MHz (Europe)</li>
<li>Uses Direwolf or multimon-ng for packet decoding</li>
<li>Interactive map shows station positions in real-time</li>
</ul>
<h3>Satellite Mode</h3>
<ul class="tip-list">
<li>Track satellites using TLE (Two-Line Element) data</li>
<li>Add satellites manually or fetch from Celestrak by category</li>
<li>Categories: Amateur, Weather, ISS, Starlink, GPS, and more</li>
<li>View next pass predictions with elevation and duration</li>
</ul>
<h3>WiFi Mode</h3>
<ul class="tip-list">
<li>Requires a WiFi adapter capable of monitor mode</li>
<li>Click "Enable Monitor" to put adapter in monitor mode</li>
<li>Scans all channels or lock to a specific channel</li>
<li>Detects drones by SSID patterns and manufacturer OUI</li>
<li>Rogue AP detection flags same SSID on multiple BSSIDs</li>
<li>Click network rows to target for deauth or handshake capture</li>
</ul>
<h3>Bluetooth Mode</h3>
<ul class="tip-list">
<li>Scans for classic Bluetooth and BLE devices</li>
<li>Shows device names, addresses, and signal strength</li>
<li>Manufacturer lookup from MAC address OUI</li>
<li>Radar visualization shows device proximity</li>
</ul>
<h3>Listening Post Mode</h3>
<ul class="tip-list">
<li>Wideband SDR scanner with spectrum visualization</li>
<li>Tune to any frequency supported by your SDR hardware</li>
<li>AM/FM/USB/LSB demodulation modes</li>
<li>Bookmark frequencies for quick recall</li>
<li>Quick tune presets for emergency and marine channels</li>
</ul>
<h3>TSCM Mode</h3>
<ul class="tip-list">
<li>Technical Surveillance Countermeasures sweep</li>
<li>Scans for unknown RF transmitters, WiFi devices, Bluetooth</li>
<li>Baseline comparison to detect new/anomalous devices</li>
<li>Threat classification: Critical, High, Medium, Low</li>
<li>Useful for security audits and bug sweeps</li>
<li><em style="color: var(--text-muted);">Note: This feature is in early development</em></li>
</ul>
<h3>Meshtastic Mode</h3>
<ul class="tip-list">
<li>Integrates with Meshtastic LoRa mesh network devices</li>
<li>Connect Heltec, T-Beam, RAK, or other compatible devices via USB</li>
<li>Real-time message streaming with RSSI and SNR metrics</li>
<li>Configure channels with encryption keys</li>
<li>View connected nodes and message history</li>
<li>Requires: Meshtastic device + <code>pip install meshtastic</code></li>
</ul>
<h3>Network Monitor</h3>
<ul class="tip-list">
<li>Aggregates data from multiple remote INTERCEPT agents</li>
<li>View all WiFi, Bluetooth, ADS-B, AIS data in one unified view</li>
<li>Real-time streaming via Server-Sent Events (SSE)</li>
<li>Location estimation using multi-agent trilateration</li>
<li>Manage agents at <code>/controller/manage</code></li>
</ul>
</div>
<!-- WiFi Section -->
<div id="help-wifi" class="help-section">
<h3>Monitor Mode</h3>
<ul class="tip-list">
<li><strong>Enable Monitor:</strong> Puts WiFi adapter in monitor mode for passive scanning</li>
<li><strong>Kill Processes:</strong> Optional - stops NetworkManager/wpa_supplicant (may drop other connections)</li>
<li>Some adapters rename when entering monitor mode (e.g., wlan0 &rarr; wlan0mon)</li>
</ul>
<h3>Handshake Capture</h3>
<ul class="tip-list">
<li>Click "Capture" on a network to start targeted handshake capture</li>
<li>Status panel shows capture progress and file location</li>
<li>Use deauth to force clients to reconnect (only on authorized networks!)</li>
<li>Handshake files saved to /tmp/intercept_handshake_*.cap</li>
</ul>
<h3>Drone Detection</h3>
<ul class="tip-list">
<li>Drones detected by SSID patterns (DJI, Parrot, Autel, etc.)</li>
<li>Also detected by manufacturer OUI in MAC address</li>
<li>Distance estimated from signal strength (approximate)</li>
<li>Click drone count in stats bar to see all detected drones</li>
</ul>
<h3>Rogue AP Detection</h3>
<ul class="tip-list">
<li>Flags networks where same SSID appears on multiple BSSIDs</li>
<li>Could indicate evil twin attack or legitimate multi-AP setup</li>
<li>Click rogue count to see which SSIDs are flagged</li>
</ul>
<h3>Proximity Alerts</h3>
<ul class="tip-list">
<li>Add MAC addresses to watch list for alerts when detected</li>
<li>Watch list persists in browser localStorage</li>
<li>Useful for tracking specific devices</li>
</ul>
<h3>Client Probe Analysis</h3>
<ul class="tip-list">
<li>Shows what networks client devices are looking for</li>
<li>Orange highlights indicate sensitive/private network names</li>
<li>Reveals user location history (home, work, hotels, airports)</li>
<li>Useful for security awareness and pen test reports</li>
</ul>
</div>
<!-- Tips Section -->
<div id="help-tips" class="help-section">
<h3>General Tips</h3>
<ul class="tip-list">
<li><strong>Collapsible sections:</strong> Click any section header (&nabla;) to collapse/expand</li>
<li><strong>Sound alerts:</strong> Toggle sound on/off in settings for each mode</li>
<li><strong>Export data:</strong> Use export buttons to save captured data as JSON</li>
<li><strong>Device Intelligence:</strong> Tracks device patterns over time</li>
<li><strong>Theme toggle:</strong> Click the theme button in header to switch dark/light mode</li>
<li><strong>Settings:</strong> Click the gear icon in the header to access settings</li>
<li><strong>Offline mode:</strong> Enable in Settings to use local assets without internet</li>
</ul>
<h3>Keyboard Shortcuts</h3>
<ul class="tip-list">
<li><strong>F1</strong> - Open this help page</li>
<li><strong>?</strong> - Open help (when not typing in a field)</li>
<li><strong>Escape</strong> - Close help and modal dialogs</li>
</ul>
<h3>Requirements</h3>
<ul class="tip-list">
<li><strong>Pager:</strong> RTL-SDR, rtl_fm, multimon-ng</li>
<li><strong>433MHz Sensors:</strong> RTL-SDR, rtl_433</li>
<li><strong>Utility Meters:</strong> RTL-SDR, rtl_tcp, rtlamr</li>
<li><strong>Aircraft (ADS-B):</strong> RTL-SDR, dump1090 or rtl_adsb</li>
<li><strong>Aircraft (ACARS):</strong> Second RTL-SDR, acarsdec</li>
<li><strong>Vessels (AIS):</strong> RTL-SDR, AIS-catcher</li>
<li><strong>APRS:</strong> RTL-SDR, direwolf or multimon-ng</li>
<li><strong>Satellite:</strong> Internet for Celestrak (optional), skyfield</li>
<li><strong>WiFi:</strong> Monitor-mode adapter, aircrack-ng suite</li>
<li><strong>Bluetooth:</strong> Bluetooth adapter, bluez (hcitool/bluetoothctl)</li>
<li><strong>Listening Post:</strong> RTL-SDR or SoapySDR-compatible hardware</li>
<li><strong>TSCM:</strong> WiFi adapter, Bluetooth adapter, RTL-SDR (all optional)</li>
<li>Run as root/sudo for full hardware access</li>
</ul>
<h3>Legal Notice</h3>
<ul class="tip-list">
<li>Only use on networks and devices you own or have authorization to test</li>
<li>Passive monitoring may be legal; active attacks require authorization</li>
<li>Check local laws regarding radio frequency monitoring</li>
</ul>
</div>
</div>
</div>
<script>
// Help modal functions - defined here so all pages have them
(function() {
// Only define if not already defined (index.html defines its own)
if (typeof window.showHelp === 'undefined') {
window.showHelp = function() {
document.getElementById('helpModal').classList.add('active');
document.body.style.overflow = 'hidden';
};
}
if (typeof window.hideHelp === 'undefined') {
window.hideHelp = function() {
document.getElementById('helpModal').classList.remove('active');
document.body.style.overflow = '';
};
}
if (typeof window.switchHelpTab === 'undefined') {
window.switchHelpTab = function(tab) {
document.querySelectorAll('.help-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.help-section').forEach(s => s.classList.remove('active'));
document.querySelector('.help-tab[data-tab="' + tab + '"]').classList.add('active');
document.getElementById('help-' + tab).classList.add('active');
};
}
// Keyboard shortcuts for help (only add once)
if (!window._helpKeyboardSetup) {
window._helpKeyboardSetup = true;
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') hideHelp();
// Open help with F1 or ? key (when not typing in an input)
var helpModal = document.getElementById('helpModal');
if (helpModal && (e.key === 'F1' || (e.key === '?' && !e.target.matches('input, textarea, select'))) && !helpModal.classList.contains('active')) {
e.preventDefault();
showHelp();
}
});
}
})();
</script>

View File

@@ -134,6 +134,11 @@
<span class="utc-time" id="headerUtcTime">--:--:--</span>
</div>
<div class="nav-divider"></div>
<a href="/" class="nav-dashboard-btn" title="Return to Main Dashboard" style="text-decoration: none;">
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg></span>
<span class="nav-label">Main Dashboard</span>
</a>
<div class="nav-divider"></div>
<div class="nav-tools">
<button class="nav-tool-btn" onclick="toggleAnimations()" title="Toggle Animations">
<span class="icon-effects-on icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg></span>
@@ -235,17 +240,33 @@
if (typeof showSettings === 'undefined') {
window.showSettings = function() {
// Navigate to main page settings
window.location.href = '/?settings=1';
// Try to open settings modal if it exists on this page
const modal = document.getElementById('settingsModal');
if (modal) {
modal.classList.add('active');
if (typeof Settings !== 'undefined' && Settings.init) {
Settings.init().then(() => {
if (Settings.checkAssets) Settings.checkAssets();
});
}
} else {
// Fall back to navigating to main page settings
window.location.href = '/?settings=1';
}
};
}
if (typeof showHelp === 'undefined') {
window.showHelp = function() {
window.open('https://smittix.github.io/intercept', '_blank');
if (typeof hideSettings === 'undefined') {
window.hideSettings = function() {
const modal = document.getElementById('settingsModal');
if (modal) {
modal.classList.remove('active');
}
};
}
// showHelp is defined by the help-modal.html partial
if (typeof logout === 'undefined') {
window.logout = function(e) {
if (e) e.preventDefault();

View File

@@ -22,6 +22,8 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/core/variables.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/help-modal.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/satellite_dashboard.css') }}">
<script>
window.INTERCEPT_SHARED_OBSERVER_LOCATION = {{ shared_observer_location | tojson }};
@@ -69,7 +71,6 @@
<span id="trackingStatus">TRACKING</span>
</div>
<div class="datetime" id="utcTime">--:--:-- UTC</div>
<a href="/?mode=satellite" class="back-link">Main Dashboard</a>
</div>
</header>
@@ -437,7 +438,7 @@
now.toISOString().substring(11, 19) + ' UTC';
}
function initGroundMap() {
async function initGroundMap() {
groundMap = L.map('groundMap', {
center: [20, 0],
zoom: 2,
@@ -448,7 +449,9 @@
// Use settings manager for tile layer (allows runtime changes)
window.groundMap = groundMap;
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
if (typeof Settings !== 'undefined') {
// Wait for settings to load from server before applying tiles
await Settings.init();
Settings.createTileLayer().addTo(groundMap);
Settings.registerMap(groundMap);
} else {
@@ -1085,6 +1088,14 @@
}
}
</script>
<!-- Settings Modal -->
{% include 'partials/settings-modal.html' %}
<!-- Help Modal -->
{% include 'partials/help-modal.html' %}
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}"></script>
<script src="{{ url_for('static', filename='js/core/global-nav.js') }}"></script>
</body>
</html>