Use Terminus font across app

This commit is contained in:
Smittix
2026-02-04 00:56:22 +00:00
parent 3ce8c1b2be
commit b5dbeb6b59
37 changed files with 22210 additions and 22184 deletions
+14 -14
View File
@@ -132,7 +132,7 @@ body {
display: flex; display: flex;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -624,7 +624,7 @@ body {
} }
.telemetry-value { .telemetry-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 12px; font-size: 12px;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
@@ -680,7 +680,7 @@ body {
} }
.aircraft-icao { .aircraft-icao {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
color: var(--text-secondary); color: var(--text-secondary);
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
@@ -700,7 +700,7 @@ body {
} }
.aircraft-detail-value { .aircraft-detail-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
color: var(--accent-cyan); color: var(--accent-cyan);
font-size: 11px; font-size: 11px;
} }
@@ -790,7 +790,7 @@ body {
border: 1px solid rgba(74, 158, 255, 0.3); border: 1px solid rgba(74, 158, 255, 0.3);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -801,7 +801,7 @@ body {
border: 1px solid rgba(74, 158, 255, 0.3); border: 1px solid rgba(74, 158, 255, 0.3);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -879,7 +879,7 @@ body {
border: none; border: none;
background: var(--accent-green); background: var(--accent-green);
color: #fff; color: #fff;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@@ -911,7 +911,7 @@ body {
border: 1px solid rgba(74, 158, 255, 0.3); border: 1px solid rgba(74, 158, 255, 0.3);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
cursor: pointer; cursor: pointer;
} }
@@ -1023,7 +1023,7 @@ body {
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
@@ -1057,7 +1057,7 @@ body {
} }
.airband-status { .airband-status {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
padding: 0 8px; padding: 0 8px;
color: var(--text-muted); color: var(--text-muted);
@@ -1407,7 +1407,7 @@ body {
} }
.strip-value { .strip-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -1545,7 +1545,7 @@ body {
.report-grid span:nth-child(even) { .report-grid span:nth-child(even) {
color: var(--text-primary); color: var(--text-primary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.report-highlights { .report-highlights {
@@ -1784,7 +1784,7 @@ body {
font-size: 11px; font-size: 11px;
font-weight: 500; font-weight: 500;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
padding-left: 8px; padding-left: 8px;
border-left: 1px solid rgba(74, 158, 255, 0.2); border-left: 1px solid rgba(74, 158, 255, 0.2);
white-space: nowrap; white-space: nowrap;
@@ -1938,7 +1938,7 @@ body {
} }
.squawk-code { .squawk-code {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-weight: 700; font-weight: 700;
color: var(--accent-cyan); color: var(--accent-cyan);
font-size: 12px; font-size: 12px;
+5 -5
View File
@@ -27,7 +27,7 @@ body {
} }
.mono { .mono {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.radar-bg { .radar-bg {
@@ -91,7 +91,7 @@ body {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -268,7 +268,7 @@ body {
} }
.status-pill { .status-pill {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
padding: 8px 12px; padding: 8px 12px;
border-radius: 999px; border-radius: 999px;
@@ -306,7 +306,7 @@ body {
} }
.panel-meta { .panel-meta {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
@@ -347,7 +347,7 @@ body {
} }
.mono { .mono {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.empty-row td, .empty-row td,
+343 -343
View File
@@ -1,343 +1,343 @@
/* /*
* Agents Management CSS * Agents Management CSS
* Styles for the remote agent management interface * Styles for the remote agent management interface
*/ */
/* CSS Variables (inherited from main theme) */ /* CSS Variables (inherited from main theme) */
:root { :root {
--bg-primary: #0a0a0f; --bg-primary: #0a0a0f;
--bg-secondary: #12121a; --bg-secondary: #12121a;
--text-primary: #e0e0e0; --text-primary: #e0e0e0;
--text-secondary: #888; --text-secondary: #888;
--border-color: #1a1a2e; --border-color: #1a1a2e;
--accent-cyan: #00d4ff; --accent-cyan: #00d4ff;
--accent-green: #00ff88; --accent-green: #00ff88;
--accent-red: #ff3366; --accent-red: #ff3366;
--accent-orange: #ff9f1c; --accent-orange: #ff9f1c;
} }
/* Agent indicator in navigation */ /* Agent indicator in navigation */
.agent-indicator { .agent-indicator {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
padding: 6px 12px; padding: 6px 12px;
background: rgba(0, 212, 255, 0.1); background: rgba(0, 212, 255, 0.1);
border: 1px solid rgba(0, 212, 255, 0.3); border: 1px solid rgba(0, 212, 255, 0.3);
border-radius: 20px; border-radius: 20px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
} }
.agent-indicator:hover { .agent-indicator:hover {
background: rgba(0, 212, 255, 0.2); background: rgba(0, 212, 255, 0.2);
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
} }
.agent-indicator-dot { .agent-indicator-dot {
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
background: var(--accent-green); background: var(--accent-green);
box-shadow: 0 0 6px var(--accent-green); box-shadow: 0 0 6px var(--accent-green);
} }
.agent-indicator-dot.remote { .agent-indicator-dot.remote {
background: var(--accent-cyan); background: var(--accent-cyan);
box-shadow: 0 0 6px var(--accent-cyan); box-shadow: 0 0 6px var(--accent-cyan);
} }
.agent-indicator-dot.multiple { .agent-indicator-dot.multiple {
background: var(--accent-orange); background: var(--accent-orange);
box-shadow: 0 0 6px var(--accent-orange); box-shadow: 0 0 6px var(--accent-orange);
} }
.agent-indicator-label { .agent-indicator-label {
font-size: 11px; font-size: 11px;
color: var(--text-primary); color: var(--text-primary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.agent-indicator-count { .agent-indicator-count {
font-size: 10px; font-size: 10px;
padding: 2px 6px; padding: 2px 6px;
background: rgba(0, 212, 255, 0.2); background: rgba(0, 212, 255, 0.2);
border-radius: 10px; border-radius: 10px;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
/* Agent selector dropdown */ /* Agent selector dropdown */
.agent-selector { .agent-selector {
position: relative; position: relative;
} }
.agent-selector-dropdown { .agent-selector-dropdown {
position: absolute; position: absolute;
top: 100%; top: 100%;
right: 0; right: 0;
margin-top: 8px; margin-top: 8px;
min-width: 280px; min-width: 280px;
background: var(--bg-secondary); background: var(--bg-secondary);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
z-index: 1000; z-index: 1000;
display: none; display: none;
} }
.agent-selector-dropdown.show { .agent-selector-dropdown.show {
display: block; display: block;
} }
.agent-selector-header { .agent-selector-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 12px 15px; padding: 12px 15px;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
.agent-selector-header h4 { .agent-selector-header h4 {
margin: 0; margin: 0;
font-size: 12px; font-size: 12px;
color: var(--accent-cyan); color: var(--accent-cyan);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 1px; letter-spacing: 1px;
} }
.agent-selector-manage { .agent-selector-manage {
font-size: 11px; font-size: 11px;
color: var(--accent-cyan); color: var(--accent-cyan);
text-decoration: none; text-decoration: none;
} }
.agent-selector-manage:hover { .agent-selector-manage:hover {
text-decoration: underline; text-decoration: underline;
} }
.agent-selector-list { .agent-selector-list {
max-height: 300px; max-height: 300px;
overflow-y: auto; overflow-y: auto;
} }
.agent-selector-item { .agent-selector-item {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 10px 15px; padding: 10px 15px;
cursor: pointer; cursor: pointer;
transition: background 0.2s; transition: background 0.2s;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
.agent-selector-item:last-child { .agent-selector-item:last-child {
border-bottom: none; border-bottom: none;
} }
.agent-selector-item:hover { .agent-selector-item:hover {
background: rgba(0, 212, 255, 0.1); background: rgba(0, 212, 255, 0.1);
} }
.agent-selector-item.selected { .agent-selector-item.selected {
background: rgba(0, 212, 255, 0.15); background: rgba(0, 212, 255, 0.15);
border-left: 3px solid var(--accent-cyan); border-left: 3px solid var(--accent-cyan);
} }
.agent-selector-item.local { .agent-selector-item.local {
border-left: 3px solid var(--accent-green); border-left: 3px solid var(--accent-green);
} }
.agent-selector-item-status { .agent-selector-item-status {
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
flex-shrink: 0; flex-shrink: 0;
} }
.agent-selector-item-status.online { .agent-selector-item-status.online {
background: var(--accent-green); background: var(--accent-green);
} }
.agent-selector-item-status.offline { .agent-selector-item-status.offline {
background: var(--accent-red); background: var(--accent-red);
} }
.agent-selector-item-info { .agent-selector-item-info {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
.agent-selector-item-name { .agent-selector-item-name {
font-size: 13px; font-size: 13px;
color: var(--text-primary); color: var(--text-primary);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.agent-selector-item-url { .agent-selector-item-url {
font-size: 10px; font-size: 10px;
color: var(--text-secondary); color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.agent-selector-item-check { .agent-selector-item-check {
color: var(--accent-green); color: var(--accent-green);
opacity: 0; opacity: 0;
} }
.agent-selector-item.selected .agent-selector-item-check { .agent-selector-item.selected .agent-selector-item-check {
opacity: 1; opacity: 1;
} }
/* Agent badge in data displays */ /* Agent badge in data displays */
.agent-badge { .agent-badge {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
padding: 2px 8px; padding: 2px 8px;
font-size: 10px; font-size: 10px;
background: rgba(0, 212, 255, 0.1); background: rgba(0, 212, 255, 0.1);
color: var(--accent-cyan); color: var(--accent-cyan);
border-radius: 10px; border-radius: 10px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.agent-badge.local, .agent-badge.local,
.agent-badge.agent-local { .agent-badge.agent-local {
background: rgba(0, 255, 136, 0.1); background: rgba(0, 255, 136, 0.1);
color: var(--accent-green); color: var(--accent-green);
} }
.agent-badge.agent-remote { .agent-badge.agent-remote {
background: rgba(0, 212, 255, 0.1); background: rgba(0, 212, 255, 0.1);
color: var(--accent-cyan); color: var(--accent-cyan);
} }
/* WiFi table agent column */ /* WiFi table agent column */
.wifi-networks-table .col-agent { .wifi-networks-table .col-agent {
width: 100px; width: 100px;
text-align: center; text-align: center;
} }
.wifi-networks-table th.col-agent { .wifi-networks-table th.col-agent {
font-size: 10px; font-size: 10px;
} }
/* Bluetooth table agent column */ /* Bluetooth table agent column */
.bt-devices-table .col-agent { .bt-devices-table .col-agent {
width: 100px; width: 100px;
text-align: center; text-align: center;
} }
.agent-badge-dot { .agent-badge-dot {
width: 6px; width: 6px;
height: 6px; height: 6px;
border-radius: 50%; border-radius: 50%;
background: currentColor; background: currentColor;
} }
/* Agent column in data tables */ /* Agent column in data tables */
.data-table .agent-col { .data-table .agent-col {
width: 120px; width: 120px;
max-width: 120px; max-width: 120px;
} }
/* Multi-agent stream indicator */ /* Multi-agent stream indicator */
.multi-agent-indicator { .multi-agent-indicator {
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
left: 20px; left: 20px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
padding: 8px 12px; padding: 8px 12px;
background: var(--bg-secondary); background: var(--bg-secondary);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 20px; border-radius: 20px;
font-size: 11px; font-size: 11px;
color: var(--text-secondary); color: var(--text-secondary);
z-index: 100; z-index: 100;
} }
.multi-agent-indicator.active { .multi-agent-indicator.active {
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
color: var(--accent-cyan); color: var(--accent-cyan);
} }
.multi-agent-indicator-pulse { .multi-agent-indicator-pulse {
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
background: var(--accent-cyan); background: var(--accent-cyan);
animation: pulse 2s infinite; animation: pulse 2s infinite;
} }
@keyframes pulse { @keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); } 0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.8); } 50% { opacity: 0.5; transform: scale(0.8); }
} }
/* Agent connection status toast */ /* Agent connection status toast */
.agent-toast { .agent-toast {
position: fixed; position: fixed;
top: 80px; top: 80px;
right: 20px; right: 20px;
padding: 10px 15px; padding: 10px 15px;
background: var(--bg-secondary); background: var(--bg-secondary);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 6px; border-radius: 6px;
font-size: 12px; font-size: 12px;
z-index: 1001; z-index: 1001;
animation: slideInRight 0.3s ease; animation: slideInRight 0.3s ease;
} }
.agent-toast.connected { .agent-toast.connected {
border-color: var(--accent-green); border-color: var(--accent-green);
color: var(--accent-green); color: var(--accent-green);
} }
.agent-toast.disconnected { .agent-toast.disconnected {
border-color: var(--accent-red); border-color: var(--accent-red);
color: var(--accent-red); color: var(--accent-red);
} }
@keyframes slideInRight { @keyframes slideInRight {
from { from {
transform: translateX(100%); transform: translateX(100%);
opacity: 0; opacity: 0;
} }
to { to {
transform: translateX(0); transform: translateX(0);
opacity: 1; opacity: 1;
} }
} }
/* Responsive adjustments */ /* Responsive adjustments */
@media (max-width: 768px) { @media (max-width: 768px) {
.agent-indicator { .agent-indicator {
padding: 4px 8px; padding: 4px 8px;
} }
.agent-indicator-label { .agent-indicator-label {
display: none; display: none;
} }
.agent-selector-dropdown { .agent-selector-dropdown {
position: fixed; position: fixed;
top: auto; top: auto;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
margin: 0; margin: 0;
border-radius: 16px 16px 0 0; border-radius: 16px 16px 0 0;
max-height: 60vh; max-height: 60vh;
} }
.agents-grid { .agents-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
+23 -23
View File
@@ -134,7 +134,7 @@ body {
display: flex; display: flex;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -183,7 +183,7 @@ body {
} }
.strip-value { .strip-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -287,7 +287,7 @@ body {
font-size: 11px; font-size: 11px;
font-weight: 500; font-weight: 500;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
padding-left: 8px; padding-left: 8px;
border-left: 1px solid rgba(74, 158, 255, 0.2); border-left: 1px solid rgba(74, 158, 255, 0.2);
white-space: nowrap; white-space: nowrap;
@@ -367,7 +367,7 @@ body {
/* Leaflet overrides - Dark map styling */ /* Leaflet overrides - Dark map styling */
.leaflet-container { .leaflet-container {
background: var(--bg-dark) !important; background: var(--bg-dark) !important;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
/* Using actual dark tiles now - no filter needed */ /* Using actual dark tiles now - no filter needed */
@@ -438,7 +438,7 @@ body {
padding: 10px 15px; padding: 10px 15px;
background: rgba(74, 158, 255, 0.05); background: rgba(74, 158, 255, 0.05);
border-bottom: 1px solid rgba(74, 158, 255, 0.1); border-bottom: 1px solid rgba(74, 158, 255, 0.1);
font-family: 'Orbitron', 'JetBrains Mono', monospace; font-family: 'Orbitron', 'Terminus', monospace;
font-size: 11px; font-size: 11px;
font-weight: 500; font-weight: 500;
letter-spacing: 2px; letter-spacing: 2px;
@@ -510,7 +510,7 @@ body {
} }
.vessel-name { .vessel-name {
font-family: 'Orbitron', 'JetBrains Mono', monospace; font-family: 'Orbitron', 'Terminus', monospace;
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -518,7 +518,7 @@ body {
} }
.vessel-mmsi { .vessel-mmsi {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--text-secondary); color: var(--text-secondary);
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
@@ -548,7 +548,7 @@ body {
} }
.detail-value { .detail-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 12px; font-size: 12px;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
@@ -604,20 +604,20 @@ body {
} }
.vessel-item-name { .vessel-item-name {
font-family: 'Orbitron', 'JetBrains Mono', monospace; font-family: 'Orbitron', 'Terminus', monospace;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
.vessel-item-type { .vessel-item-type {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
color: var(--text-secondary); color: var(--text-secondary);
} }
.vessel-item-speed { .vessel-item-speed {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--accent-cyan); color: var(--accent-cyan);
text-align: right; text-align: right;
@@ -687,7 +687,7 @@ body {
border: 1px solid rgba(74, 158, 255, 0.3); border: 1px solid rgba(74, 158, 255, 0.3);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -698,7 +698,7 @@ body {
border: 1px solid rgba(74, 158, 255, 0.3); border: 1px solid rgba(74, 158, 255, 0.3);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -717,7 +717,7 @@ body {
border: none; border: none;
background: var(--accent-green); background: var(--accent-green);
color: #fff; color: #fff;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@@ -1004,7 +1004,7 @@ body {
padding: 6px 12px; padding: 6px 12px;
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid rgba(245, 158, 11, 0.1); border-bottom: 1px solid rgba(245, 158, 11, 0.1);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
} }
@@ -1079,7 +1079,7 @@ body {
} }
.dsc-message-category { .dsc-message-category {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
@@ -1096,13 +1096,13 @@ body {
} }
.dsc-message-time { .dsc-message-time {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
color: var(--text-dim); color: var(--text-dim);
} }
.dsc-message-mmsi { .dsc-message-mmsi {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--accent-orange); color: var(--accent-orange);
} }
@@ -1120,7 +1120,7 @@ body {
} }
.dsc-message-pos { .dsc-message-pos {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
color: var(--text-secondary); color: var(--text-secondary);
} }
@@ -1148,7 +1148,7 @@ body {
} }
.dsc-distress-alert .dsc-alert-header { .dsc-distress-alert .dsc-alert-header {
font-family: 'Orbitron', 'JetBrains Mono', monospace; font-family: 'Orbitron', 'Terminus', monospace;
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
color: var(--accent-red); color: var(--accent-red);
@@ -1157,7 +1157,7 @@ body {
} }
.dsc-distress-alert .dsc-alert-mmsi { .dsc-distress-alert .dsc-alert-mmsi {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 16px; font-size: 16px;
color: var(--accent-cyan); color: var(--accent-cyan);
margin-bottom: 8px; margin-bottom: 8px;
@@ -1177,7 +1177,7 @@ body {
} }
.dsc-distress-alert .dsc-alert-position { .dsc-distress-alert .dsc-alert-position {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
color: var(--accent-cyan); color: var(--accent-cyan);
margin-bottom: 16px; margin-bottom: 16px;
@@ -1188,7 +1188,7 @@ body {
border: none; border: none;
color: white; color: white;
padding: 10px 24px; padding: 10px 24px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+371 -371
View File
@@ -1,371 +1,371 @@
/* Function Strip (Action Bar) - Shared across modes /* Function Strip (Action Bar) - Shared across modes
* Based on APRS strip pattern, reusable for Pager, Sensor, Bluetooth, WiFi, TSCM, etc. * Based on APRS strip pattern, reusable for Pager, Sensor, Bluetooth, WiFi, TSCM, etc.
*/ */
.function-strip { .function-strip {
background: linear-gradient(180deg, var(--bg-panel) 0%, var(--bg-dark) 100%); background: linear-gradient(180deg, var(--bg-panel) 0%, var(--bg-dark) 100%);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 6px; border-radius: 6px;
padding: 8px 12px; padding: 8px 12px;
margin-bottom: 10px; margin-bottom: 10px;
overflow: visible; overflow: visible;
min-height: 44px; min-height: 44px;
} }
.function-strip-inner { .function-strip-inner {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
min-width: max-content; min-width: max-content;
} }
/* Stats */ /* Stats */
.function-strip .strip-stat { .function-strip .strip-stat {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 6px 10px; padding: 6px 10px;
background: rgba(74, 158, 255, 0.05); background: rgba(74, 158, 255, 0.05);
border: 1px solid rgba(74, 158, 255, 0.15); border: 1px solid rgba(74, 158, 255, 0.15);
border-radius: 4px; border-radius: 4px;
min-width: 55px; min-width: 55px;
} }
.function-strip .strip-stat:hover { .function-strip .strip-stat:hover {
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
border-color: rgba(74, 158, 255, 0.3); border-color: rgba(74, 158, 255, 0.3);
} }
.function-strip .strip-value { .function-strip .strip-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
line-height: 1.2; line-height: 1.2;
} }
.function-strip .strip-label { .function-strip .strip-label {
font-size: 8px; font-size: 8px;
font-weight: 600; font-weight: 600;
color: var(--text-dim); color: var(--text-dim);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
margin-top: 1px; margin-top: 1px;
} }
.function-strip .strip-divider { .function-strip .strip-divider {
width: 1px; width: 1px;
height: 28px; height: 28px;
background: var(--border-color); background: var(--border-color);
margin: 0 4px; margin: 0 4px;
} }
/* Signal stat coloring */ /* Signal stat coloring */
.function-strip .signal-stat.good .strip-value { color: var(--accent-green); } .function-strip .signal-stat.good .strip-value { color: var(--accent-green); }
.function-strip .signal-stat.warning .strip-value { color: var(--accent-yellow); } .function-strip .signal-stat.warning .strip-value { color: var(--accent-yellow); }
.function-strip .signal-stat.poor .strip-value { color: var(--accent-red); } .function-strip .signal-stat.poor .strip-value { color: var(--accent-red); }
/* Controls */ /* Controls */
.function-strip .strip-control { .function-strip .strip-control {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
} }
.function-strip .strip-select { .function-strip .strip-select {
background: rgba(0,0,0,0.3); background: rgba(0,0,0,0.3);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-primary); color: var(--text-primary);
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
cursor: pointer; cursor: pointer;
} }
.function-strip .strip-select:hover { .function-strip .strip-select:hover {
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
} }
.function-strip .strip-select:disabled { .function-strip .strip-select:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
.function-strip .strip-input-label { .function-strip .strip-input-label {
font-size: 9px; font-size: 9px;
color: var(--text-muted); color: var(--text-muted);
font-weight: 600; font-weight: 600;
} }
.function-strip .strip-input { .function-strip .strip-input {
background: rgba(0,0,0,0.3); background: rgba(0,0,0,0.3);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-primary); color: var(--text-primary);
padding: 4px 6px; padding: 4px 6px;
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
width: 50px; width: 50px;
text-align: center; text-align: center;
} }
.function-strip .strip-input:hover, .function-strip .strip-input:hover,
.function-strip .strip-input:focus { .function-strip .strip-input:focus {
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
outline: none; outline: none;
} }
.function-strip .strip-input:disabled { .function-strip .strip-input:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
/* Wider input for frequency values */ /* Wider input for frequency values */
.function-strip .strip-input.wide { .function-strip .strip-input.wide {
width: 70px; width: 70px;
} }
/* Tool Status Indicators */ /* Tool Status Indicators */
.function-strip .strip-tools { .function-strip .strip-tools {
display: flex; display: flex;
gap: 4px; gap: 4px;
} }
.function-strip .strip-tool { .function-strip .strip-tool {
font-size: 9px; font-size: 9px;
font-weight: 600; font-weight: 600;
padding: 3px 6px; padding: 3px 6px;
border-radius: 3px; border-radius: 3px;
background: rgba(255, 59, 48, 0.2); background: rgba(255, 59, 48, 0.2);
color: var(--accent-red); color: var(--accent-red);
border: 1px solid rgba(255, 59, 48, 0.3); border: 1px solid rgba(255, 59, 48, 0.3);
} }
.function-strip .strip-tool.ok { .function-strip .strip-tool.ok {
background: rgba(0, 255, 136, 0.1); background: rgba(0, 255, 136, 0.1);
color: var(--accent-green); color: var(--accent-green);
border-color: rgba(0, 255, 136, 0.3); border-color: rgba(0, 255, 136, 0.3);
} }
.function-strip .strip-tool.warn { .function-strip .strip-tool.warn {
background: rgba(255, 193, 7, 0.2); background: rgba(255, 193, 7, 0.2);
color: var(--accent-yellow); color: var(--accent-yellow);
border-color: rgba(255, 193, 7, 0.3); border-color: rgba(255, 193, 7, 0.3);
} }
/* Buttons */ /* Buttons */
.function-strip .strip-btn { .function-strip .strip-btn {
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
border: 1px solid rgba(74, 158, 255, 0.2); border: 1px solid rgba(74, 158, 255, 0.2);
color: var(--text-primary); color: var(--text-primary);
padding: 6px 12px; padding: 6px 12px;
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
white-space: nowrap; white-space: nowrap;
} }
.function-strip .strip-btn:hover:not(:disabled) { .function-strip .strip-btn:hover:not(:disabled) {
background: rgba(74, 158, 255, 0.2); background: rgba(74, 158, 255, 0.2);
border-color: rgba(74, 158, 255, 0.4); border-color: rgba(74, 158, 255, 0.4);
} }
.function-strip .strip-btn.primary { .function-strip .strip-btn.primary {
background: linear-gradient(135deg, var(--accent-green) 0%, #10b981 100%); background: linear-gradient(135deg, var(--accent-green) 0%, #10b981 100%);
border: none; border: none;
color: #000; color: #000;
} }
.function-strip .strip-btn.primary:hover:not(:disabled) { .function-strip .strip-btn.primary:hover:not(:disabled) {
filter: brightness(1.1); filter: brightness(1.1);
} }
.function-strip .strip-btn.stop { .function-strip .strip-btn.stop {
background: linear-gradient(135deg, var(--accent-red) 0%, #dc2626 100%); background: linear-gradient(135deg, var(--accent-red) 0%, #dc2626 100%);
border: none; border: none;
color: #fff; color: #fff;
} }
.function-strip .strip-btn.stop:hover:not(:disabled) { .function-strip .strip-btn.stop:hover:not(:disabled) {
filter: brightness(1.1); filter: brightness(1.1);
} }
.function-strip .strip-btn:disabled { .function-strip .strip-btn:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
/* Status indicator */ /* Status indicator */
.function-strip .strip-status { .function-strip .strip-status {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
padding: 4px 8px; padding: 4px 8px;
background: rgba(0,0,0,0.2); background: rgba(0,0,0,0.2);
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
color: var(--text-secondary); color: var(--text-secondary);
} }
.function-strip .status-dot { .function-strip .status-dot {
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
background: var(--text-muted); background: var(--text-muted);
} }
.function-strip .status-dot.inactive { .function-strip .status-dot.inactive {
background: var(--text-muted); background: var(--text-muted);
} }
.function-strip .status-dot.active, .function-strip .status-dot.active,
.function-strip .status-dot.scanning, .function-strip .status-dot.scanning,
.function-strip .status-dot.decoding { .function-strip .status-dot.decoding {
background: var(--accent-cyan); background: var(--accent-cyan);
animation: strip-pulse 1.5s ease-in-out infinite; animation: strip-pulse 1.5s ease-in-out infinite;
} }
.function-strip .status-dot.listening, .function-strip .status-dot.listening,
.function-strip .status-dot.tracking, .function-strip .status-dot.tracking,
.function-strip .status-dot.receiving { .function-strip .status-dot.receiving {
background: var(--accent-green); background: var(--accent-green);
animation: strip-pulse 1.5s ease-in-out infinite; animation: strip-pulse 1.5s ease-in-out infinite;
} }
.function-strip .status-dot.sweeping { .function-strip .status-dot.sweeping {
background: var(--accent-orange); background: var(--accent-orange);
animation: strip-pulse 1s ease-in-out infinite; animation: strip-pulse 1s ease-in-out infinite;
} }
.function-strip .status-dot.error { .function-strip .status-dot.error {
background: var(--accent-red); background: var(--accent-red);
} }
@keyframes strip-pulse { @keyframes strip-pulse {
0%, 100% { opacity: 1; box-shadow: 0 0 4px 2px currentColor; } 0%, 100% { opacity: 1; box-shadow: 0 0 4px 2px currentColor; }
50% { opacity: 0.6; box-shadow: none; } 50% { opacity: 0.6; box-shadow: none; }
} }
/* Time display */ /* Time display */
.function-strip .strip-time { .function-strip .strip-time {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--text-muted); color: var(--text-muted);
padding: 4px 8px; padding: 4px 8px;
background: rgba(0,0,0,0.2); background: rgba(0,0,0,0.2);
border-radius: 4px; border-radius: 4px;
white-space: nowrap; white-space: nowrap;
} }
/* Mode-specific accent colors */ /* Mode-specific accent colors */
.function-strip.pager-strip .strip-stat { .function-strip.pager-strip .strip-stat {
background: rgba(255, 193, 7, 0.05); background: rgba(255, 193, 7, 0.05);
border-color: rgba(255, 193, 7, 0.15); border-color: rgba(255, 193, 7, 0.15);
} }
.function-strip.pager-strip .strip-stat:hover { .function-strip.pager-strip .strip-stat:hover {
background: rgba(255, 193, 7, 0.1); background: rgba(255, 193, 7, 0.1);
border-color: rgba(255, 193, 7, 0.3); border-color: rgba(255, 193, 7, 0.3);
} }
.function-strip.pager-strip .strip-value { .function-strip.pager-strip .strip-value {
color: var(--accent-yellow); color: var(--accent-yellow);
} }
.function-strip.sensor-strip .strip-stat { .function-strip.sensor-strip .strip-stat {
background: rgba(0, 255, 136, 0.05); background: rgba(0, 255, 136, 0.05);
border-color: rgba(0, 255, 136, 0.15); border-color: rgba(0, 255, 136, 0.15);
} }
.function-strip.sensor-strip .strip-stat:hover { .function-strip.sensor-strip .strip-stat:hover {
background: rgba(0, 255, 136, 0.1); background: rgba(0, 255, 136, 0.1);
border-color: rgba(0, 255, 136, 0.3); border-color: rgba(0, 255, 136, 0.3);
} }
.function-strip.sensor-strip .strip-value { .function-strip.sensor-strip .strip-value {
color: var(--accent-green); color: var(--accent-green);
} }
.function-strip.bt-strip .strip-stat { .function-strip.bt-strip .strip-stat {
background: rgba(0, 122, 255, 0.05); background: rgba(0, 122, 255, 0.05);
border-color: rgba(0, 122, 255, 0.15); border-color: rgba(0, 122, 255, 0.15);
} }
.function-strip.bt-strip .strip-stat:hover { .function-strip.bt-strip .strip-stat:hover {
background: rgba(0, 122, 255, 0.1); background: rgba(0, 122, 255, 0.1);
border-color: rgba(0, 122, 255, 0.3); border-color: rgba(0, 122, 255, 0.3);
} }
.function-strip.bt-strip .strip-value { .function-strip.bt-strip .strip-value {
color: #0a84ff; color: #0a84ff;
} }
.function-strip.wifi-strip .strip-stat { .function-strip.wifi-strip .strip-stat {
background: rgba(255, 149, 0, 0.05); background: rgba(255, 149, 0, 0.05);
border-color: rgba(255, 149, 0, 0.15); border-color: rgba(255, 149, 0, 0.15);
} }
.function-strip.wifi-strip .strip-stat:hover { .function-strip.wifi-strip .strip-stat:hover {
background: rgba(255, 149, 0, 0.1); background: rgba(255, 149, 0, 0.1);
border-color: rgba(255, 149, 0, 0.3); border-color: rgba(255, 149, 0, 0.3);
} }
.function-strip.wifi-strip .strip-value { .function-strip.wifi-strip .strip-value {
color: var(--accent-orange); color: var(--accent-orange);
} }
.function-strip.tscm-strip { .function-strip.tscm-strip {
margin-top: 4px; /* Extra clearance to prevent top clipping */ margin-top: 4px; /* Extra clearance to prevent top clipping */
} }
.function-strip.tscm-strip .strip-stat { .function-strip.tscm-strip .strip-stat {
background: rgba(255, 59, 48, 0.15); background: rgba(255, 59, 48, 0.15);
border: 1px solid rgba(255, 59, 48, 0.4); border: 1px solid rgba(255, 59, 48, 0.4);
} }
.function-strip.tscm-strip .strip-stat:hover { .function-strip.tscm-strip .strip-stat:hover {
background: rgba(255, 59, 48, 0.25); background: rgba(255, 59, 48, 0.25);
border-color: rgba(255, 59, 48, 0.6); border-color: rgba(255, 59, 48, 0.6);
} }
.function-strip.tscm-strip .strip-value { .function-strip.tscm-strip .strip-value {
color: #ef4444; /* Explicit red color */ color: #ef4444; /* Explicit red color */
} }
.function-strip.tscm-strip .strip-label { .function-strip.tscm-strip .strip-label {
color: #9ca3af; /* Explicit light gray */ color: #9ca3af; /* Explicit light gray */
} }
.function-strip.tscm-strip .strip-select { .function-strip.tscm-strip .strip-select {
color: #e8eaed; /* Explicit white for selects */ color: #e8eaed; /* Explicit white for selects */
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
} }
.function-strip.tscm-strip .strip-btn { .function-strip.tscm-strip .strip-btn {
color: #e8eaed; /* Explicit white for buttons */ color: #e8eaed; /* Explicit white for buttons */
} }
.function-strip.tscm-strip .strip-tool { .function-strip.tscm-strip .strip-tool {
color: #e8eaed; /* Explicit white for tool indicators */ color: #e8eaed; /* Explicit white for tool indicators */
} }
.function-strip.tscm-strip .strip-time, .function-strip.tscm-strip .strip-time,
.function-strip.tscm-strip .strip-status span { .function-strip.tscm-strip .strip-status span {
color: #9ca3af; /* Explicit gray for status/time */ color: #9ca3af; /* Explicit gray for status/time */
} }
.function-strip.rtlamr-strip .strip-stat { .function-strip.rtlamr-strip .strip-stat {
background: rgba(175, 82, 222, 0.05); background: rgba(175, 82, 222, 0.05);
border-color: rgba(175, 82, 222, 0.15); border-color: rgba(175, 82, 222, 0.15);
} }
.function-strip.rtlamr-strip .strip-stat:hover { .function-strip.rtlamr-strip .strip-stat:hover {
background: rgba(175, 82, 222, 0.1); background: rgba(175, 82, 222, 0.1);
border-color: rgba(175, 82, 222, 0.3); border-color: rgba(175, 82, 222, 0.3);
} }
.function-strip.rtlamr-strip .strip-value { .function-strip.rtlamr-strip .strip-value {
color: #af52de; color: #af52de;
} }
.function-strip.listening-strip .strip-stat { .function-strip.listening-strip .strip-stat {
background: rgba(74, 158, 255, 0.05); background: rgba(74, 158, 255, 0.05);
border-color: rgba(74, 158, 255, 0.15); border-color: rgba(74, 158, 255, 0.15);
} }
.function-strip.listening-strip .strip-stat:hover { .function-strip.listening-strip .strip-stat:hover {
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
border-color: rgba(74, 158, 255, 0.3); border-color: rgba(74, 158, 255, 0.3);
} }
.function-strip.listening-strip .strip-value { .function-strip.listening-strip .strip-value {
color: var(--accent-cyan); color: var(--accent-cyan);
} }
/* Threat-colored stats for TSCM */ /* Threat-colored stats for TSCM */
.function-strip .strip-stat.threat-high .strip-value { color: var(--accent-red); } .function-strip .strip-stat.threat-high .strip-value { color: var(--accent-red); }
.function-strip .strip-stat.threat-review .strip-value { color: var(--accent-orange); } .function-strip .strip-stat.threat-review .strip-value { color: var(--accent-orange); }
.function-strip .strip-stat.threat-info .strip-value { color: var(--accent-cyan); } .function-strip .strip-stat.threat-info .strip-value { color: var(--accent-cyan); }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -73,8 +73,8 @@
/* ============================================ /* ============================================
TYPOGRAPHY TYPOGRAPHY
============================================ */ ============================================ */
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; --font-mono: 'Terminus', 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
/* Font sizes */ /* Font sizes */
--text-xs: 10px; --text-xs: 10px;
+17 -8
View File
@@ -33,7 +33,7 @@
src: url('/static/vendor/fonts/Inter-Bold.woff2') format('woff2'); src: url('/static/vendor/fonts/Inter-Bold.woff2') format('woff2');
} }
/* JetBrains Mono - Monospace/code font */ /* JetBrains Mono - Monospace/code font */
@font-face { @font-face {
font-family: 'JetBrains Mono'; font-family: 'JetBrains Mono';
font-style: normal; font-style: normal;
@@ -58,10 +58,19 @@
src: url('/static/vendor/fonts/JetBrainsMono-SemiBold.woff2') format('woff2'); src: url('/static/vendor/fonts/JetBrainsMono-SemiBold.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'JetBrains Mono'; font-family: 'JetBrains Mono';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
src: url('/static/vendor/fonts/JetBrainsMono-Bold.woff2') format('woff2'); src: url('/static/vendor/fonts/JetBrainsMono-Bold.woff2') format('woff2');
} }
/* Terminus - Monospace UI font */
@font-face {
font-family: 'Terminus';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/static/vendor/fonts/Terminus.ttf') format('truetype');
}
+5 -5
View File
@@ -50,7 +50,7 @@
letter-spacing: 1px; letter-spacing: 1px;
margin-right: 8px; margin-right: 8px;
font-weight: 500; font-weight: 500;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.mode-nav-divider { .mode-nav-divider {
@@ -80,7 +80,7 @@
.mode-nav-btn .nav-label { .mode-nav-btn .nav-label {
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.08em; letter-spacing: 0.08em;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -127,7 +127,7 @@
.nav-action-btn .nav-label { .nav-action-btn .nav-label {
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.08em; letter-spacing: 0.08em;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -162,7 +162,7 @@
.mode-nav-dropdown-btn .nav-label { .mode-nav-dropdown-btn .nav-label {
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.08em; letter-spacing: 0.08em;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
@@ -268,7 +268,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
flex-shrink: 0; flex-shrink: 0;
white-space: nowrap; white-space: nowrap;
+59 -51
View File
@@ -1,5 +1,13 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
@font-face {
font-family: 'Terminus';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/static/vendor/fonts/Terminus.ttf') format('truetype');
}
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@@ -259,7 +267,7 @@ body {
} }
.welcome-title { .welcome-title {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 2.5rem; font-size: 2.5rem;
font-weight: 700; font-weight: 700;
color: var(--text-primary); color: var(--text-primary);
@@ -269,7 +277,7 @@ body {
} }
.welcome-tagline { .welcome-tagline {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 0.9rem; font-size: 0.9rem;
color: var(--accent-cyan); color: var(--accent-cyan);
letter-spacing: 0.15em; letter-spacing: 0.15em;
@@ -278,7 +286,7 @@ body {
.welcome-version { .welcome-version {
display: inline-block; display: inline-block;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 0.65rem; font-size: 0.65rem;
color: var(--bg-primary); color: var(--bg-primary);
background: var(--accent-cyan); background: var(--accent-cyan);
@@ -297,7 +305,7 @@ body {
} }
.welcome-content h2 { .welcome-content h2 {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 0.75rem; font-size: 0.75rem;
color: var(--text-secondary); color: var(--text-secondary);
text-transform: uppercase; text-transform: uppercase;
@@ -333,7 +341,7 @@ body {
} }
.changelog-version { .changelog-version {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 0.8rem; font-size: 0.8rem;
color: var(--accent-cyan); color: var(--accent-cyan);
font-weight: 600; font-weight: 600;
@@ -364,7 +372,7 @@ body {
position: absolute; position: absolute;
left: -15px; left: -15px;
color: var(--accent-green); color: var(--accent-green);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
/* Mode Selection Grid */ /* Mode Selection Grid */
@@ -435,7 +443,7 @@ body {
} }
.mode-card .mode-name { .mode-card .mode-name {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 0.75rem; font-size: 0.75rem;
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: var(--text-primary);
@@ -463,7 +471,7 @@ body {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 600; font-weight: 600;
color: var(--text-secondary); color: var(--text-secondary);
@@ -922,7 +930,7 @@ header h1 {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
flex-shrink: 0; flex-shrink: 0;
white-space: nowrap; white-space: nowrap;
@@ -1030,7 +1038,7 @@ header h1 {
.version-badge { .version-badge {
font-size: 0.6rem; font-size: 0.6rem;
font-weight: 500; font-weight: 500;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--text-secondary); color: var(--text-secondary);
background: var(--bg-tertiary); background: var(--bg-tertiary);
@@ -1089,7 +1097,7 @@ header h1 .tagline {
background: var(--bg-tertiary); background: var(--bg-tertiary);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 6px; border-radius: 6px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
transition: all 0.2s ease; transition: all 0.2s ease;
} }
@@ -1578,7 +1586,7 @@ header h1 .tagline {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
color: var(--text-primary); color: var(--text-primary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 12px; font-size: 12px;
transition: all 0.15s ease; transition: all 0.15s ease;
} }
@@ -1637,7 +1645,7 @@ header h1 .tagline {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-secondary); color: var(--text-secondary);
cursor: pointer; cursor: pointer;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
@@ -1757,7 +1765,7 @@ header h1 .tagline {
gap: 8px; gap: 8px;
font-size: 10px; font-size: 10px;
color: var(--text-secondary); color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.stats>div { .stats>div {
@@ -1783,7 +1791,7 @@ header h1 .tagline {
flex: 1; flex: 1;
padding: 10px; padding: 10px;
overflow-y: auto; overflow-y: auto;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
background: var(--bg-primary); background: var(--bg-primary);
min-height: 0; /* Allow shrinking in flex context */ min-height: 0; /* Allow shrinking in flex context */
@@ -1855,7 +1863,7 @@ header h1 .tagline {
.message .address { .message .address {
color: var(--accent-green); color: var(--accent-green);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
margin-bottom: 8px; margin-bottom: 8px;
} }
@@ -1868,7 +1876,7 @@ header h1 .tagline {
} }
.message .content.numeric { .message .content.numeric {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 15px; font-size: 15px;
letter-spacing: 2px; letter-spacing: 2px;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -2357,7 +2365,7 @@ header h1 .tagline {
/* Dark theme for Leaflet */ /* Dark theme for Leaflet */
.leaflet-container { .leaflet-container {
background: #0a0a0a; background: #0a0a0a;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
/* Using actual dark tiles now - no filter needed */ /* Using actual dark tiles now - no filter needed */
@@ -2394,7 +2402,7 @@ header h1 .tagline {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
z-index: 1000; z-index: 1000;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--accent-cyan); color: var(--accent-cyan);
text-shadow: 0 0 5px var(--accent-cyan); text-shadow: 0 0 5px var(--accent-cyan);
@@ -2411,7 +2419,7 @@ header h1 .tagline {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
z-index: 1000; z-index: 1000;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--accent-cyan); color: var(--accent-cyan);
text-shadow: 0 0 5px var(--accent-cyan); text-shadow: 0 0 5px var(--accent-cyan);
@@ -2432,7 +2440,7 @@ header h1 .tagline {
} }
.aircraft-popup { .aircraft-popup {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -2476,7 +2484,7 @@ header h1 .tagline {
background: rgba(0, 0, 0, 0.8) !important; background: rgba(0, 0, 0, 0.8) !important;
border: 1px solid var(--accent-cyan) !important; border: 1px solid var(--accent-cyan) !important;
color: var(--accent-cyan) !important; color: var(--accent-cyan) !important;
font-family: 'JetBrains Mono', monospace !important; font-family: var(--font-mono) !important;
font-size: 10px !important; font-size: 10px !important;
padding: 2px 6px !important; padding: 2px 6px !important;
border-radius: 2px !important; border-radius: 2px !important;
@@ -2720,7 +2728,7 @@ header h1 .tagline {
color: var(--accent-cyan); color: var(--accent-cyan);
font-size: 22px; font-size: 22px;
font-weight: 700; font-weight: 700;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
text-shadow: 0 0 15px var(--accent-cyan-dim); text-shadow: 0 0 15px var(--accent-cyan-dim);
line-height: 1.2; line-height: 1.2;
} }
@@ -3114,7 +3122,7 @@ header h1 .tagline {
} }
.sensor-card .data-value { .sensor-card .data-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
@@ -3164,7 +3172,7 @@ header h1 .tagline {
display: flex; display: flex;
gap: 15px; gap: 15px;
font-size: 10px; font-size: 10px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.recon-stats span { .recon-stats span {
@@ -3214,14 +3222,14 @@ header h1 .tagline {
.device-id { .device-id {
color: var(--text-dim); color: var(--text-dim);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
} }
.device-meta { .device-meta {
text-align: right; text-align: right;
color: var(--text-secondary); color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.device-meta.encrypted { .device-meta.encrypted {
@@ -3297,7 +3305,7 @@ header h1 .tagline {
} }
.hex-dump { .hex-dump {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--text-dim); color: var(--text-dim);
background: var(--bg-primary); background: var(--bg-primary);
@@ -3969,7 +3977,7 @@ header h1 .tagline {
} }
.bt-detail-address { .bt-detail-address {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: #00d4ff; color: #00d4ff;
} }
@@ -3983,7 +3991,7 @@ header h1 .tagline {
} }
.bt-detail-rssi-value { .bt-detail-rssi-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
} }
@@ -4078,7 +4086,7 @@ header h1 .tagline {
} }
.bt-detail-services-list { .bt-detail-services-list {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 8px; font-size: 8px;
color: var(--text-dim); color: var(--text-dim);
white-space: nowrap; white-space: nowrap;
@@ -4325,7 +4333,7 @@ header h1 .tagline {
} }
.bt-rssi-value { .bt-rssi-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
min-width: 28px; min-width: 28px;
@@ -4684,7 +4692,7 @@ header h1 .tagline {
flex-direction: column; flex-direction: column;
gap: 4px; gap: 4px;
font-size: 10px; font-size: 10px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
.security-legend-item { .security-legend-item {
@@ -4731,7 +4739,7 @@ header h1 .tagline {
} }
.signal-value { .signal-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 28px; font-size: 28px;
color: var(--accent-cyan); color: var(--accent-cyan);
text-shadow: 0 0 10px var(--accent-cyan-dim); text-shadow: 0 0 10px var(--accent-cyan-dim);
@@ -5247,7 +5255,7 @@ body::before {
.meter-value { .meter-value {
font-size: 10px; font-size: 10px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
color: var(--text-secondary); color: var(--text-secondary);
width: 50px; width: 50px;
text-align: right; text-align: right;
@@ -5404,7 +5412,7 @@ body::before {
} }
.freq-digits { .freq-digits {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 56px; font-size: 56px;
font-weight: 700; font-weight: 700;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -5425,7 +5433,7 @@ body::before {
} }
.freq-unit { .freq-unit {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 20px; font-size: 20px;
color: var(--text-secondary); color: var(--text-secondary);
margin-left: 8px; margin-left: 8px;
@@ -5569,7 +5577,7 @@ body::before {
} }
.knob-value { .knob-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -5694,7 +5702,7 @@ body::before {
background: var(--bg-secondary); background: var(--bg-secondary);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-secondary); color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
border-radius: 4px; border-radius: 4px;
@@ -5756,13 +5764,13 @@ body::before {
} }
.signal-arc-label { .signal-arc-label {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 8px; font-size: 8px;
fill: var(--text-muted); fill: var(--text-muted);
} }
.signal-arc-value { .signal-arc-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
fill: var(--accent-cyan); fill: var(--accent-cyan);
@@ -5794,7 +5802,7 @@ body::before {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
@@ -5930,7 +5938,7 @@ body::before {
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
padding: 10px 15px; padding: 10px 15px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -6019,7 +6027,7 @@ body::before {
} }
.module-header { .module-header {
font-family: 'Orbitron', 'JetBrains Mono', monospace; font-family: 'Orbitron', 'Terminus', monospace;
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
@@ -6044,7 +6052,7 @@ body::before {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
color: var(--text-primary); color: var(--text-primary);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 12px; font-size: 12px;
padding: 6px 8px; padding: 6px 8px;
text-align: center; text-align: center;
@@ -6102,7 +6110,7 @@ body::before {
} }
.stat-value { .stat-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 22px; font-size: 22px;
font-weight: bold; font-weight: bold;
} }
@@ -6150,7 +6158,7 @@ body::before {
.tune-btn { .tune-btn {
padding: 4px 8px; padding: 4px 8px;
font-size: 10px; font-size: 10px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
background: var(--bg-elevated); background: var(--bg-elevated);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-secondary); color: var(--text-secondary);
@@ -6180,13 +6188,13 @@ body::before {
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
border-radius: 4px; border-radius: 4px;
padding: 8px; padding: 8px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
/* Listening Mode Selector Buttons */ /* Listening Mode Selector Buttons */
.radio-mode-btn { .radio-mode-btn {
padding: 12px 24px; padding: 12px 24px;
font-family: 'Orbitron', 'JetBrains Mono', monospace; font-family: 'Orbitron', 'Terminus', monospace;
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@@ -6227,7 +6235,7 @@ body::before {
/* Frequency Preset Buttons */ /* Frequency Preset Buttons */
.preset-freq-btn { .preset-freq-btn {
padding: 8px 14px; padding: 8px 14px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
background: var(--bg-elevated); background: var(--bg-elevated);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@@ -6291,4 +6299,4 @@ body::before {
[data-animations="off"] .logo-dot, [data-animations="off"] .logo-dot,
[data-animations="off"] .welcome-logo { [data-animations="off"] .welcome-logo {
animation: none !important; animation: none !important;
} }
+6 -6
View File
@@ -37,7 +37,7 @@
/* Typography */ /* Typography */
.landing-title { .landing-title {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 2.2rem; font-size: 2.2rem;
font-weight: 700; font-weight: 700;
letter-spacing: 0.4em; letter-spacing: 0.4em;
@@ -48,7 +48,7 @@
} }
.landing-tagline { .landing-tagline {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
color: var(--accent-cyan); color: var(--accent-cyan);
font-size: 0.9rem; font-size: 0.9rem;
letter-spacing: 0.15em; letter-spacing: 0.15em;
@@ -71,7 +71,7 @@
/* Hacker Style Error */ /* Hacker Style Error */
.flash-error { .flash-error {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--accent-red); color: var(--accent-red);
background: rgba(239, 68, 68, 0.1); background: rgba(239, 68, 68, 0.1);
@@ -94,7 +94,7 @@
color: var(--accent-cyan); color: var(--accent-cyan);
padding: 12px; padding: 12px;
margin-bottom: 15px; margin-bottom: 15px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
outline: none; outline: none;
box-sizing: border-box; /* Crucial for visibility */ box-sizing: border-box; /* Crucial for visibility */
@@ -106,7 +106,7 @@
border: 2px solid var(--accent-cyan); border: 2px solid var(--accent-cyan);
color: var(--accent-cyan); color: var(--accent-cyan);
padding: 15px; padding: 15px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-weight: 600; font-weight: 600;
letter-spacing: 3px; letter-spacing: 3px;
cursor: pointer; cursor: pointer;
@@ -116,7 +116,7 @@
.landing-version { .landing-version {
margin-top: 25px; margin-top: 25px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: rgba(255, 255, 255, 0.3); color: rgba(255, 255, 255, 0.3);
letter-spacing: 2px; letter-spacing: 2px;
+328 -328
View File
@@ -1,328 +1,328 @@
/* APRS Function Bar (Stats Strip) Styles */ /* APRS Function Bar (Stats Strip) Styles */
.aprs-strip { .aprs-strip {
background: linear-gradient(180deg, var(--bg-panel) 0%, var(--bg-dark) 100%); background: linear-gradient(180deg, var(--bg-panel) 0%, var(--bg-dark) 100%);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 6px; border-radius: 6px;
padding: 6px 12px; padding: 6px 12px;
margin-bottom: 10px; margin-bottom: 10px;
overflow-x: auto; overflow-x: auto;
} }
.aprs-strip-inner { .aprs-strip-inner {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
min-width: max-content; min-width: max-content;
} }
.aprs-strip .strip-stat { .aprs-strip .strip-stat {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 4px 10px; padding: 4px 10px;
background: rgba(74, 158, 255, 0.05); background: rgba(74, 158, 255, 0.05);
border: 1px solid rgba(74, 158, 255, 0.15); border: 1px solid rgba(74, 158, 255, 0.15);
border-radius: 4px; border-radius: 4px;
min-width: 55px; min-width: 55px;
} }
.aprs-strip .strip-stat:hover { .aprs-strip .strip-stat:hover {
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
border-color: rgba(74, 158, 255, 0.3); border-color: rgba(74, 158, 255, 0.3);
} }
.aprs-strip .strip-value { .aprs-strip .strip-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: var(--accent-cyan); color: var(--accent-cyan);
line-height: 1.2; line-height: 1.2;
} }
.aprs-strip .strip-label { .aprs-strip .strip-label {
font-size: 8px; font-size: 8px;
font-weight: 600; font-weight: 600;
color: var(--text-dim); color: var(--text-dim);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
margin-top: 1px; margin-top: 1px;
} }
.aprs-strip .strip-divider { .aprs-strip .strip-divider {
width: 1px; width: 1px;
height: 28px; height: 28px;
background: var(--border-color); background: var(--border-color);
margin: 0 4px; margin: 0 4px;
} }
/* Signal stat coloring */ /* Signal stat coloring */
.aprs-strip .signal-stat.good .strip-value { color: var(--accent-green); } .aprs-strip .signal-stat.good .strip-value { color: var(--accent-green); }
.aprs-strip .signal-stat.warning .strip-value { color: var(--accent-yellow); } .aprs-strip .signal-stat.warning .strip-value { color: var(--accent-yellow); }
.aprs-strip .signal-stat.poor .strip-value { color: var(--accent-red); } .aprs-strip .signal-stat.poor .strip-value { color: var(--accent-red); }
/* Controls */ /* Controls */
.aprs-strip .strip-control { .aprs-strip .strip-control {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
} }
.aprs-strip .strip-select { .aprs-strip .strip-select {
background: rgba(0,0,0,0.3); background: rgba(0,0,0,0.3);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-primary); color: var(--text-primary);
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
cursor: pointer; cursor: pointer;
} }
.aprs-strip .strip-select:hover { .aprs-strip .strip-select:hover {
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
} }
.aprs-strip .strip-input-label { .aprs-strip .strip-input-label {
font-size: 9px; font-size: 9px;
color: var(--text-muted); color: var(--text-muted);
font-weight: 600; font-weight: 600;
} }
.aprs-strip .strip-input { .aprs-strip .strip-input {
background: rgba(0,0,0,0.3); background: rgba(0,0,0,0.3);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--text-primary); color: var(--text-primary);
padding: 4px 6px; padding: 4px 6px;
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
width: 50px; width: 50px;
text-align: center; text-align: center;
} }
.aprs-strip .strip-input:hover, .aprs-strip .strip-input:hover,
.aprs-strip .strip-input:focus { .aprs-strip .strip-input:focus {
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
outline: none; outline: none;
} }
/* Tool Status Indicators */ /* Tool Status Indicators */
.aprs-strip .strip-tools { .aprs-strip .strip-tools {
display: flex; display: flex;
gap: 4px; gap: 4px;
} }
.aprs-strip .strip-tool { .aprs-strip .strip-tool {
font-size: 9px; font-size: 9px;
font-weight: 600; font-weight: 600;
padding: 3px 6px; padding: 3px 6px;
border-radius: 3px; border-radius: 3px;
background: rgba(255, 59, 48, 0.2); background: rgba(255, 59, 48, 0.2);
color: var(--accent-red); color: var(--accent-red);
border: 1px solid rgba(255, 59, 48, 0.3); border: 1px solid rgba(255, 59, 48, 0.3);
} }
.aprs-strip .strip-tool.ok { .aprs-strip .strip-tool.ok {
background: rgba(0, 255, 136, 0.1); background: rgba(0, 255, 136, 0.1);
color: var(--accent-green); color: var(--accent-green);
border-color: rgba(0, 255, 136, 0.3); border-color: rgba(0, 255, 136, 0.3);
} }
/* Buttons */ /* Buttons */
.aprs-strip .strip-btn { .aprs-strip .strip-btn {
background: rgba(74, 158, 255, 0.1); background: rgba(74, 158, 255, 0.1);
border: 1px solid rgba(74, 158, 255, 0.2); border: 1px solid rgba(74, 158, 255, 0.2);
color: var(--text-primary); color: var(--text-primary);
padding: 6px 12px; padding: 6px 12px;
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
white-space: nowrap; white-space: nowrap;
} }
.aprs-strip .strip-btn:hover:not(:disabled) { .aprs-strip .strip-btn:hover:not(:disabled) {
background: rgba(74, 158, 255, 0.2); background: rgba(74, 158, 255, 0.2);
border-color: rgba(74, 158, 255, 0.4); border-color: rgba(74, 158, 255, 0.4);
} }
.aprs-strip .strip-btn.primary { .aprs-strip .strip-btn.primary {
background: linear-gradient(135deg, var(--accent-green) 0%, #10b981 100%); background: linear-gradient(135deg, var(--accent-green) 0%, #10b981 100%);
border: none; border: none;
color: #000; color: #000;
} }
.aprs-strip .strip-btn.primary:hover:not(:disabled) { .aprs-strip .strip-btn.primary:hover:not(:disabled) {
filter: brightness(1.1); filter: brightness(1.1);
} }
.aprs-strip .strip-btn.stop { .aprs-strip .strip-btn.stop {
background: linear-gradient(135deg, var(--accent-red) 0%, #dc2626 100%); background: linear-gradient(135deg, var(--accent-red) 0%, #dc2626 100%);
border: none; border: none;
color: #fff; color: #fff;
} }
.aprs-strip .strip-btn.stop:hover:not(:disabled) { .aprs-strip .strip-btn.stop:hover:not(:disabled) {
filter: brightness(1.1); filter: brightness(1.1);
} }
.aprs-strip .strip-btn:disabled { .aprs-strip .strip-btn:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
/* Status indicator */ /* Status indicator */
.aprs-strip .strip-status { .aprs-strip .strip-status {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
padding: 4px 8px; padding: 4px 8px;
background: rgba(0,0,0,0.2); background: rgba(0,0,0,0.2);
border-radius: 4px; border-radius: 4px;
font-size: 10px; font-size: 10px;
font-weight: 600; font-weight: 600;
color: var(--text-secondary); color: var(--text-secondary);
} }
.aprs-strip .status-dot { .aprs-strip .status-dot {
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
background: var(--text-muted); background: var(--text-muted);
} }
.aprs-strip .status-dot.listening { .aprs-strip .status-dot.listening {
background: var(--accent-cyan); background: var(--accent-cyan);
animation: aprs-strip-pulse 1.5s ease-in-out infinite; animation: aprs-strip-pulse 1.5s ease-in-out infinite;
} }
.aprs-strip .status-dot.tracking { .aprs-strip .status-dot.tracking {
background: var(--accent-green); background: var(--accent-green);
animation: aprs-strip-pulse 1.5s ease-in-out infinite; animation: aprs-strip-pulse 1.5s ease-in-out infinite;
} }
.aprs-strip .status-dot.error { .aprs-strip .status-dot.error {
background: var(--accent-red); background: var(--accent-red);
} }
@keyframes aprs-strip-pulse { @keyframes aprs-strip-pulse {
0%, 100% { opacity: 1; box-shadow: 0 0 4px 2px currentColor; } 0%, 100% { opacity: 1; box-shadow: 0 0 4px 2px currentColor; }
50% { opacity: 0.6; box-shadow: none; } 50% { opacity: 0.6; box-shadow: none; }
} }
/* Time display */ /* Time display */
.aprs-strip .strip-time { .aprs-strip .strip-time {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--text-muted); color: var(--text-muted);
padding: 4px 8px; padding: 4px 8px;
background: rgba(0,0,0,0.2); background: rgba(0,0,0,0.2);
border-radius: 4px; border-radius: 4px;
white-space: nowrap; white-space: nowrap;
} }
/* APRS Status Bar Styles (Sidebar - legacy) */ /* APRS Status Bar Styles (Sidebar - legacy) */
.aprs-status-bar { .aprs-status-bar {
margin-top: 12px; margin-top: 12px;
padding: 10px; padding: 10px;
background: rgba(0,0,0,0.3); background: rgba(0,0,0,0.3);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
} }
.aprs-status-indicator { .aprs-status-indicator {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
margin-bottom: 8px; margin-bottom: 8px;
} }
.aprs-status-dot { .aprs-status-dot {
width: 10px; width: 10px;
height: 10px; height: 10px;
border-radius: 50%; border-radius: 50%;
background: var(--text-muted); background: var(--text-muted);
} }
.aprs-status-dot.standby { background: var(--text-muted); } .aprs-status-dot.standby { background: var(--text-muted); }
.aprs-status-dot.listening { .aprs-status-dot.listening {
background: var(--accent-cyan); background: var(--accent-cyan);
animation: aprs-pulse 1.5s ease-in-out infinite; animation: aprs-pulse 1.5s ease-in-out infinite;
} }
.aprs-status-dot.tracking { background: var(--accent-green); } .aprs-status-dot.tracking { background: var(--accent-green); }
.aprs-status-dot.error { background: var(--accent-red); } .aprs-status-dot.error { background: var(--accent-red); }
@keyframes aprs-pulse { @keyframes aprs-pulse {
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(74, 158, 255, 0.7); } 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(74, 158, 255, 0.7); }
50% { opacity: 0.6; box-shadow: 0 0 8px 4px rgba(74, 158, 255, 0.3); } 50% { opacity: 0.6; box-shadow: 0 0 8px 4px rgba(74, 158, 255, 0.3); }
} }
.aprs-status-text { .aprs-status-text {
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
letter-spacing: 1px; letter-spacing: 1px;
} }
.aprs-status-stats { .aprs-status-stats {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
font-size: 9px; font-size: 9px;
} }
.aprs-stat { .aprs-stat {
color: var(--text-secondary); color: var(--text-secondary);
} }
.aprs-stat-label { .aprs-stat-label {
color: var(--text-muted); color: var(--text-muted);
} }
/* Signal Meter Styles */ /* Signal Meter Styles */
.aprs-signal-meter { .aprs-signal-meter {
margin-top: 12px; margin-top: 12px;
padding: 10px; padding: 10px;
background: rgba(0,0,0,0.3); background: rgba(0,0,0,0.3);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
} }
.aprs-meter-header { .aprs-meter-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
margin-bottom: 8px; margin-bottom: 8px;
} }
.aprs-meter-label { .aprs-meter-label {
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
letter-spacing: 1px; letter-spacing: 1px;
color: var(--text-secondary); color: var(--text-secondary);
} }
.aprs-meter-value { .aprs-meter-value {
font-size: 12px; font-size: 12px;
font-weight: bold; font-weight: bold;
font-family: monospace; font-family: monospace;
color: var(--accent-cyan); color: var(--accent-cyan);
min-width: 24px; min-width: 24px;
} }
.aprs-meter-burst { .aprs-meter-burst {
font-size: 9px; font-size: 9px;
font-weight: bold; font-weight: bold;
color: var(--accent-yellow); color: var(--accent-yellow);
background: rgba(255, 193, 7, 0.2); background: rgba(255, 193, 7, 0.2);
padding: 2px 6px; padding: 2px 6px;
border-radius: 3px; border-radius: 3px;
animation: burst-flash 0.3s ease-out; animation: burst-flash 0.3s ease-out;
} }
@keyframes burst-flash { @keyframes burst-flash {
0% { opacity: 1; transform: scale(1.1); } 0% { opacity: 1; transform: scale(1.1); }
100% { opacity: 1; transform: scale(1); } 100% { opacity: 1; transform: scale(1); }
} }
.aprs-meter-bar-container { .aprs-meter-bar-container {
position: relative; position: relative;
height: 16px; height: 16px;
background: rgba(0,0,0,0.4); background: rgba(0,0,0,0.4);
border-radius: 3px; border-radius: 3px;
overflow: hidden; overflow: hidden;
margin-bottom: 4px; margin-bottom: 4px;
} }
.aprs-meter-bar { .aprs-meter-bar {
height: 100%; height: 100%;
width: 0%; width: 0%;
background: linear-gradient(90deg, background: linear-gradient(90deg,
var(--accent-green) 0%, var(--accent-green) 0%,
var(--accent-cyan) 50%, var(--accent-cyan) 50%,
var(--accent-yellow) 75%, var(--accent-yellow) 75%,
var(--accent-red) 100% var(--accent-red) 100%
); );
border-radius: 3px; border-radius: 3px;
transition: width 0.1s ease-out; transition: width 0.1s ease-out;
} }
.aprs-meter-bar.no-signal { .aprs-meter-bar.no-signal {
opacity: 0.3; opacity: 0.3;
} }
.aprs-meter-ticks { .aprs-meter-ticks {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-size: 8px; font-size: 8px;
color: var(--text-muted); color: var(--text-muted);
padding: 0 2px; padding: 0 2px;
} }
.aprs-meter-status { .aprs-meter-status {
font-size: 9px; font-size: 9px;
color: var(--text-muted); color: var(--text-muted);
text-align: center; text-align: center;
margin-top: 6px; margin-top: 6px;
} }
.aprs-meter-status.active { .aprs-meter-status.active {
color: var(--accent-green); color: var(--accent-green);
} }
.aprs-meter-status.no-signal { .aprs-meter-status.no-signal {
color: var(--accent-yellow); color: var(--accent-yellow);
} }
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -27,7 +27,7 @@
} }
.spy-stations-title { .spy-stations-title {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: var(--text-primary);
@@ -101,7 +101,7 @@
} }
.spy-station-name { .spy-station-name {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: var(--text-primary);
@@ -117,7 +117,7 @@
/* Type Badge */ /* Type Badge */
.spy-station-badge { .spy-station-badge {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@@ -173,7 +173,7 @@
} }
.spy-meta-mode { .spy-meta-mode {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
color: var(--accent-orange); color: var(--accent-orange);
} }
@@ -186,7 +186,7 @@
} }
.spy-freq-list { .spy-freq-list {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--accent-cyan); color: var(--accent-cyan);
line-height: 1.6; line-height: 1.6;
@@ -199,7 +199,7 @@
} }
.spy-freq-item { .spy-freq-item {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--accent-cyan); color: var(--accent-cyan);
background: var(--bg-secondary); background: var(--bg-secondary);
@@ -236,7 +236,7 @@
} }
.spy-freq-select { .spy-freq-select {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
padding: 6px 8px; padding: 6px 8px;
background: var(--bg-secondary); background: var(--bg-secondary);
@@ -273,7 +273,7 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
+876 -876
View File
File diff suppressed because it is too large Load Diff
+1463 -1463
View File
File diff suppressed because it is too large Load Diff
+13 -4
View File
@@ -1,7 +1,16 @@
/* ============================================ /* ============================================
RESPONSIVE UTILITIES - iNTERCEPT RESPONSIVE UTILITIES - iNTERCEPT
Shared responsive foundation for all pages Shared responsive foundation for all pages
============================================ */ ============================================ */
/* Terminus - bundled monospace */
@font-face {
font-family: 'Terminus';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/static/vendor/fonts/Terminus.ttf') format('truetype');
}
/* ============== CSS VARIABLES ============== */ /* ============== CSS VARIABLES ============== */
:root { :root {
+5 -5
View File
@@ -142,7 +142,7 @@ body {
border: 1px solid rgba(0, 212, 255, 0.3); border: 1px solid rgba(0, 212, 255, 0.3);
border-radius: 4px; border-radius: 4px;
padding: 4px 10px; padding: 4px 10px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -164,7 +164,7 @@ body {
display: flex; display: flex;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
@@ -457,7 +457,7 @@ body {
} }
.telemetry-value { .telemetry-value {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 12px; font-size: 12px;
color: var(--accent-cyan); color: var(--accent-cyan);
} }
@@ -543,7 +543,7 @@ body {
} }
.pass-time { .pass-time {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
} }
/* Bottom controls bar */ /* Bottom controls bar */
@@ -579,7 +579,7 @@ body {
border: 1px solid rgba(0, 212, 255, 0.3); border: 1px solid rgba(0, 212, 255, 0.3);
border-radius: 4px; border-radius: 4px;
color: var(--accent-cyan); color: var(--accent-cyan);
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
} }
+444 -444
View File
@@ -1,444 +1,444 @@
/* Settings Modal Styles */ /* Settings Modal Styles */
.settings-modal { .settings-modal {
display: none; display: none;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.85); background: rgba(0, 0, 0, 0.85);
z-index: 10000; z-index: 10000;
overflow-y: auto; overflow-y: auto;
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
} }
.settings-modal.active { .settings-modal.active {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
padding: 40px 20px; padding: 40px 20px;
} }
.settings-content { .settings-content {
background: var(--bg-dark, #0a0a0f); background: var(--bg-dark, #0a0a0f);
border: 1px solid var(--border-color, #1a1a2e); border: 1px solid var(--border-color, #1a1a2e);
border-radius: 8px; border-radius: 8px;
max-width: 600px; max-width: 600px;
width: 100%; width: 100%;
position: relative; position: relative;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
} }
.settings-header { .settings-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 16px 20px; padding: 16px 20px;
border-bottom: 1px solid var(--border-color, #1a1a2e); border-bottom: 1px solid var(--border-color, #1a1a2e);
} }
.settings-header h2 { .settings-header h2 {
margin: 0; margin: 0;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: var(--text-primary, #e0e0e0); color: var(--text-primary, #e0e0e0);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.settings-header h2 .icon { .settings-header h2 .icon {
width: 20px; width: 20px;
height: 20px; height: 20px;
color: var(--accent-cyan, #00d4ff); color: var(--accent-cyan, #00d4ff);
} }
.settings-close { .settings-close {
background: none; background: none;
border: none; border: none;
color: var(--text-muted, #666); color: var(--text-muted, #666);
font-size: 24px; font-size: 24px;
cursor: pointer; cursor: pointer;
padding: 4px; padding: 4px;
line-height: 1; line-height: 1;
transition: color 0.2s; transition: color 0.2s;
} }
.settings-close:hover { .settings-close:hover {
color: var(--accent-red, #ff4444); color: var(--accent-red, #ff4444);
} }
/* Settings Tabs */ /* Settings Tabs */
.settings-tabs { .settings-tabs {
display: flex; display: flex;
border-bottom: 1px solid var(--border-color, #1a1a2e); border-bottom: 1px solid var(--border-color, #1a1a2e);
padding: 0 20px; padding: 0 20px;
gap: 4px; gap: 4px;
} }
.settings-tab { .settings-tab {
background: none; background: none;
border: none; border: none;
padding: 12px 16px; padding: 12px 16px;
color: var(--text-muted, #666); color: var(--text-muted, #666);
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
transition: color 0.2s; transition: color 0.2s;
} }
.settings-tab:hover { .settings-tab:hover {
color: var(--text-primary, #e0e0e0); color: var(--text-primary, #e0e0e0);
} }
.settings-tab.active { .settings-tab.active {
color: var(--accent-cyan, #00d4ff); color: var(--accent-cyan, #00d4ff);
} }
.settings-tab.active::after { .settings-tab.active::after {
content: ''; content: '';
position: absolute; position: absolute;
bottom: -1px; bottom: -1px;
left: 0; left: 0;
right: 0; right: 0;
height: 2px; height: 2px;
background: var(--accent-cyan, #00d4ff); background: var(--accent-cyan, #00d4ff);
} }
/* Settings Sections */ /* Settings Sections */
.settings-section { .settings-section {
display: none; display: none;
padding: 20px; padding: 20px;
} }
.settings-section.active { .settings-section.active {
display: block; display: block;
} }
.settings-group { .settings-group {
margin-bottom: 24px; margin-bottom: 24px;
} }
.settings-group:last-child { .settings-group:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.settings-group-title { .settings-group-title {
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
color: var(--text-muted, #666); color: var(--text-muted, #666);
margin-bottom: 12px; margin-bottom: 12px;
} }
/* Settings Row */ /* Settings Row */
.settings-row { .settings-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 12px 0; padding: 12px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05);
} }
.settings-row:last-child { .settings-row:last-child {
border-bottom: none; border-bottom: none;
} }
.settings-label { .settings-label {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 2px; gap: 2px;
} }
.settings-label-text { .settings-label-text {
font-size: 13px; font-size: 13px;
color: var(--text-primary, #e0e0e0); color: var(--text-primary, #e0e0e0);
} }
.settings-label-desc { .settings-label-desc {
font-size: 11px; font-size: 11px;
color: var(--text-muted, #666); color: var(--text-muted, #666);
} }
/* Toggle Switch */ /* Toggle Switch */
.toggle-switch { .toggle-switch {
position: relative; position: relative;
width: 44px; width: 44px;
height: 24px; height: 24px;
flex-shrink: 0; flex-shrink: 0;
} }
.toggle-switch input { .toggle-switch input {
opacity: 0; opacity: 0;
width: 0; width: 0;
height: 0; height: 0;
} }
.toggle-slider { .toggle-slider {
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background-color: var(--bg-tertiary, #1a1a2e); background-color: var(--bg-tertiary, #1a1a2e);
border: 1px solid var(--border-color, #2a2a3e); border: 1px solid var(--border-color, #2a2a3e);
transition: 0.3s; transition: 0.3s;
border-radius: 24px; border-radius: 24px;
} }
.toggle-slider:before { .toggle-slider:before {
position: absolute; position: absolute;
content: ""; content: "";
height: 18px; height: 18px;
width: 18px; width: 18px;
left: 2px; left: 2px;
bottom: 2px; bottom: 2px;
background-color: var(--text-muted, #666); background-color: var(--text-muted, #666);
transition: 0.3s; transition: 0.3s;
border-radius: 50%; border-radius: 50%;
} }
.toggle-switch input:checked + .toggle-slider { .toggle-switch input:checked + .toggle-slider {
background-color: var(--accent-cyan, #00d4ff); background-color: var(--accent-cyan, #00d4ff);
border-color: var(--accent-cyan, #00d4ff); border-color: var(--accent-cyan, #00d4ff);
} }
.toggle-switch input:checked + .toggle-slider:before { .toggle-switch input:checked + .toggle-slider:before {
transform: translateX(20px); transform: translateX(20px);
background-color: white; background-color: white;
} }
.toggle-switch input:focus + .toggle-slider { .toggle-switch input:focus + .toggle-slider {
box-shadow: 0 0 0 2px rgba(0, 212, 255, 0.3); box-shadow: 0 0 0 2px rgba(0, 212, 255, 0.3);
} }
/* Select Dropdown */ /* Select Dropdown */
.settings-select { .settings-select {
background: var(--bg-tertiary, #1a1a2e); background: var(--bg-tertiary, #1a1a2e);
border: 1px solid var(--border-color, #2a2a3e); border: 1px solid var(--border-color, #2a2a3e);
border-radius: 4px; border-radius: 4px;
padding: 8px 12px; padding: 8px 12px;
font-size: 13px; font-size: 13px;
color: var(--text-primary, #e0e0e0); color: var(--text-primary, #e0e0e0);
min-width: 160px; min-width: 160px;
cursor: pointer; cursor: pointer;
appearance: none; appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right 8px center; background-position: right 8px center;
padding-right: 32px; padding-right: 32px;
} }
.settings-select:focus { .settings-select:focus {
outline: none; outline: none;
border-color: var(--accent-cyan, #00d4ff); border-color: var(--accent-cyan, #00d4ff);
} }
/* Text Input */ /* Text Input */
.settings-input { .settings-input {
background: var(--bg-tertiary, #1a1a2e); background: var(--bg-tertiary, #1a1a2e);
border: 1px solid var(--border-color, #2a2a3e); border: 1px solid var(--border-color, #2a2a3e);
border-radius: 4px; border-radius: 4px;
padding: 8px 12px; padding: 8px 12px;
font-size: 13px; font-size: 13px;
color: var(--text-primary, #e0e0e0); color: var(--text-primary, #e0e0e0);
width: 200px; width: 200px;
} }
.settings-input:focus { .settings-input:focus {
outline: none; outline: none;
border-color: var(--accent-cyan, #00d4ff); border-color: var(--accent-cyan, #00d4ff);
} }
.settings-input::placeholder { .settings-input::placeholder {
color: var(--text-muted, #666); color: var(--text-muted, #666);
} }
/* Asset Status */ /* Asset Status */
.asset-status { .asset-status {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
margin-top: 12px; margin-top: 12px;
padding: 12px; padding: 12px;
background: var(--bg-secondary, #0f0f1a); background: var(--bg-secondary, #0f0f1a);
border-radius: 6px; border-radius: 6px;
} }
.asset-status-row { .asset-status-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-size: 12px; font-size: 12px;
} }
.asset-name { .asset-name {
color: var(--text-muted, #888); color: var(--text-muted, #888);
} }
.asset-badge { .asset-badge {
padding: 2px 8px; padding: 2px 8px;
border-radius: 10px; border-radius: 10px;
font-size: 10px; font-size: 10px;
font-weight: 500; font-weight: 500;
text-transform: uppercase; text-transform: uppercase;
} }
.asset-badge.available { .asset-badge.available {
background: rgba(0, 255, 136, 0.15); background: rgba(0, 255, 136, 0.15);
color: var(--accent-green, #00ff88); color: var(--accent-green, #00ff88);
} }
.asset-badge.missing { .asset-badge.missing {
background: rgba(255, 68, 68, 0.15); background: rgba(255, 68, 68, 0.15);
color: var(--accent-red, #ff4444); color: var(--accent-red, #ff4444);
} }
.asset-badge.checking { .asset-badge.checking {
background: rgba(255, 170, 0, 0.15); background: rgba(255, 170, 0, 0.15);
color: var(--accent-orange, #ffaa00); color: var(--accent-orange, #ffaa00);
} }
/* Check Assets Button */ /* Check Assets Button */
.check-assets-btn { .check-assets-btn {
background: var(--bg-tertiary, #1a1a2e); background: var(--bg-tertiary, #1a1a2e);
border: 1px solid var(--border-color, #2a2a3e); border: 1px solid var(--border-color, #2a2a3e);
color: var(--text-primary, #e0e0e0); color: var(--text-primary, #e0e0e0);
padding: 8px 16px; padding: 8px 16px;
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
cursor: pointer; cursor: pointer;
margin-top: 12px; margin-top: 12px;
transition: all 0.2s; transition: all 0.2s;
} }
.check-assets-btn:hover { .check-assets-btn:hover {
border-color: var(--accent-cyan, #00d4ff); border-color: var(--accent-cyan, #00d4ff);
color: var(--accent-cyan, #00d4ff); color: var(--accent-cyan, #00d4ff);
} }
.check-assets-btn:disabled { .check-assets-btn:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
/* GPS Detection Spinner */ /* GPS Detection Spinner */
.detecting-spinner { .detecting-spinner {
display: inline-block; display: inline-block;
width: 12px; width: 12px;
height: 12px; height: 12px;
border: 2px solid currentColor; border: 2px solid currentColor;
border-top-color: transparent; border-top-color: transparent;
border-radius: 50%; border-radius: 50%;
animation: detecting-spin 0.8s linear infinite; animation: detecting-spin 0.8s linear infinite;
vertical-align: middle; vertical-align: middle;
margin-right: 6px; margin-right: 6px;
} }
@keyframes detecting-spin { @keyframes detecting-spin {
to { transform: rotate(360deg); } to { transform: rotate(360deg); }
} }
/* About Section */ /* About Section */
.about-info { .about-info {
font-size: 13px; font-size: 13px;
color: var(--text-muted, #888); color: var(--text-muted, #888);
line-height: 1.6; line-height: 1.6;
} }
.about-info p { .about-info p {
margin: 0 0 12px 0; margin: 0 0 12px 0;
} }
.about-info a { .about-info a {
color: var(--accent-cyan, #00d4ff); color: var(--accent-cyan, #00d4ff);
text-decoration: none; text-decoration: none;
} }
.about-info a:hover { .about-info a:hover {
text-decoration: underline; text-decoration: underline;
} }
.about-version { .about-version {
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
color: var(--accent-cyan, #00d4ff); color: var(--accent-cyan, #00d4ff);
} }
/* Donate Button */ /* Donate Button */
.donate-btn { .donate-btn {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 10px 20px; padding: 10px 20px;
background: linear-gradient(135deg, var(--accent-amber, #d4a853) 0%, var(--accent-orange, #f59e0b) 100%); background: linear-gradient(135deg, var(--accent-amber, #d4a853) 0%, var(--accent-orange, #f59e0b) 100%);
border: none; border: none;
border-radius: 6px; border-radius: 6px;
color: #000; color: #000;
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(212, 168, 83, 0.3); box-shadow: 0 2px 8px rgba(212, 168, 83, 0.3);
} }
.donate-btn:hover { .donate-btn:hover {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(212, 168, 83, 0.4); box-shadow: 0 4px 12px rgba(212, 168, 83, 0.4);
filter: brightness(1.1); filter: brightness(1.1);
} }
.donate-btn:active { .donate-btn:active {
transform: translateY(0); transform: translateY(0);
} }
/* Tile Provider Custom URL */ /* Tile Provider Custom URL */
.custom-url-row { .custom-url-row {
margin-top: 8px; margin-top: 8px;
padding-top: 8px; padding-top: 8px;
} }
.custom-url-row .settings-input { .custom-url-row .settings-input {
width: 100%; width: 100%;
} }
/* Info Callout */ /* Info Callout */
.settings-info { .settings-info {
background: rgba(0, 212, 255, 0.1); background: rgba(0, 212, 255, 0.1);
border: 1px solid rgba(0, 212, 255, 0.2); border: 1px solid rgba(0, 212, 255, 0.2);
border-radius: 6px; border-radius: 6px;
padding: 12px; padding: 12px;
margin-top: 16px; margin-top: 16px;
font-size: 12px; font-size: 12px;
color: var(--text-muted, #888); color: var(--text-muted, #888);
} }
.settings-info strong { .settings-info strong {
color: var(--accent-cyan, #00d4ff); color: var(--accent-cyan, #00d4ff);
} }
/* Responsive */ /* Responsive */
@media (max-width: 640px) { @media (max-width: 640px) {
.settings-modal.active { .settings-modal.active {
padding: 20px 10px; padding: 20px 10px;
} }
.settings-content { .settings-content {
max-width: 100%; max-width: 100%;
} }
.settings-row { .settings-row {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 8px; gap: 8px;
} }
.settings-select, .settings-select,
.settings-input { .settings-input {
width: 100%; width: 100%;
} }
} }
+494 -494
View File
@@ -1,36 +1,36 @@
/** /**
* Intercept - Core Application Logic * Intercept - Core Application Logic
* Global state, mode switching, and shared functionality * Global state, mode switching, and shared functionality
*/ */
// ============== GLOBAL STATE ============== // ============== GLOBAL STATE ==============
// Mode state flags // Mode state flags
let eventSource = null; let eventSource = null;
let isRunning = false; let isRunning = false;
let isSensorRunning = false; let isSensorRunning = false;
let isAdsbRunning = false; let isAdsbRunning = false;
let isWifiRunning = false; let isWifiRunning = false;
let isBtRunning = false; let isBtRunning = false;
let currentMode = 'pager'; let currentMode = 'pager';
// Message counters // Message counters
let msgCount = 0; let msgCount = 0;
let pocsagCount = 0; let pocsagCount = 0;
let flexCount = 0; let flexCount = 0;
let sensorCount = 0; let sensorCount = 0;
let filteredCount = 0; let filteredCount = 0;
// Device list (populated from server via Jinja2) // Device list (populated from server via Jinja2)
let deviceList = []; let deviceList = [];
// Auto-scroll setting // Auto-scroll setting
let autoScroll = localStorage.getItem('autoScroll') !== 'false'; let autoScroll = localStorage.getItem('autoScroll') !== 'false';
// Mute setting // Mute setting
let muted = localStorage.getItem('audioMuted') === 'true'; let muted = localStorage.getItem('audioMuted') === 'true';
// Observer location (load from localStorage or default to London) // Observer location (load from localStorage or default to London)
let observerLocation = (function() { let observerLocation = (function() {
if (window.ObserverLocation && ObserverLocation.getForModule) { if (window.ObserverLocation && ObserverLocation.getForModule) {
return ObserverLocation.getForModule('observerLocation'); return ObserverLocation.getForModule('observerLocation');
@@ -44,464 +44,464 @@ let observerLocation = (function() {
} }
return { lat: 51.5074, lon: -0.1278 }; return { lat: 51.5074, lon: -0.1278 };
})(); })();
// Message storage for export // Message storage for export
let allMessages = []; let allMessages = [];
// Track unique sensor devices // Track unique sensor devices
let uniqueDevices = new Set(); let uniqueDevices = new Set();
// SDR device usage tracking // SDR device usage tracking
let sdrDeviceUsage = {}; let sdrDeviceUsage = {};
// ============== DISCLAIMER HANDLING ============== // ============== DISCLAIMER HANDLING ==============
function checkDisclaimer() { function checkDisclaimer() {
const accepted = localStorage.getItem('disclaimerAccepted'); const accepted = localStorage.getItem('disclaimerAccepted');
if (accepted === 'true') { if (accepted === 'true') {
document.getElementById('disclaimerModal').classList.add('disclaimer-hidden'); document.getElementById('disclaimerModal').classList.add('disclaimer-hidden');
} }
} }
function acceptDisclaimer() { function acceptDisclaimer() {
localStorage.setItem('disclaimerAccepted', 'true'); localStorage.setItem('disclaimerAccepted', 'true');
document.getElementById('disclaimerModal').classList.add('disclaimer-hidden'); document.getElementById('disclaimerModal').classList.add('disclaimer-hidden');
} }
function declineDisclaimer() { function declineDisclaimer() {
document.getElementById('disclaimerModal').classList.add('disclaimer-hidden'); document.getElementById('disclaimerModal').classList.add('disclaimer-hidden');
document.getElementById('rejectionPage').classList.remove('disclaimer-hidden'); document.getElementById('rejectionPage').classList.remove('disclaimer-hidden');
} }
// ============== HEADER CLOCK ============== // ============== HEADER CLOCK ==============
function updateHeaderClock() { function updateHeaderClock() {
const now = new Date(); const now = new Date();
const utc = now.toISOString().substring(11, 19); const utc = now.toISOString().substring(11, 19);
document.getElementById('headerUtcTime').textContent = utc; document.getElementById('headerUtcTime').textContent = utc;
} }
// ============== MODE SWITCHING ============== // ============== MODE SWITCHING ==============
function switchMode(mode) { function switchMode(mode) {
// Stop any running scans when switching modes // Stop any running scans when switching modes
if (isRunning && typeof stopDecoding === 'function') stopDecoding(); if (isRunning && typeof stopDecoding === 'function') stopDecoding();
if (isSensorRunning && typeof stopSensorDecoding === 'function') stopSensorDecoding(); if (isSensorRunning && typeof stopSensorDecoding === 'function') stopSensorDecoding();
if (isWifiRunning && typeof stopWifiScan === 'function') stopWifiScan(); if (isWifiRunning && typeof stopWifiScan === 'function') stopWifiScan();
if (isBtRunning && typeof stopBtScan === 'function') stopBtScan(); if (isBtRunning && typeof stopBtScan === 'function') stopBtScan();
if (isAdsbRunning && typeof stopAdsbScan === 'function') stopAdsbScan(); if (isAdsbRunning && typeof stopAdsbScan === 'function') stopAdsbScan();
currentMode = mode; currentMode = mode;
// Remove active from all nav buttons, then add to the correct one // Remove active from all nav buttons, then add to the correct one
document.querySelectorAll('.mode-nav-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.mode-nav-btn').forEach(btn => btn.classList.remove('active'));
const modeMap = { const modeMap = {
'pager': 'pager', 'sensor': '433', 'aircraft': 'aircraft', 'pager': 'pager', 'sensor': '433', 'aircraft': 'aircraft',
'satellite': 'satellite', 'wifi': 'wifi', 'bluetooth': 'bluetooth', 'satellite': 'satellite', 'wifi': 'wifi', 'bluetooth': 'bluetooth',
'listening': 'listening', 'meshtastic': 'meshtastic' 'listening': 'listening', 'meshtastic': 'meshtastic'
}; };
document.querySelectorAll('.mode-nav-btn').forEach(btn => { document.querySelectorAll('.mode-nav-btn').forEach(btn => {
const label = btn.querySelector('.nav-label'); const label = btn.querySelector('.nav-label');
if (label && label.textContent.toLowerCase().includes(modeMap[mode])) { if (label && label.textContent.toLowerCase().includes(modeMap[mode])) {
btn.classList.add('active'); btn.classList.add('active');
} }
}); });
// Toggle mode content visibility // Toggle mode content visibility
document.getElementById('pagerMode').classList.toggle('active', mode === 'pager'); document.getElementById('pagerMode').classList.toggle('active', mode === 'pager');
document.getElementById('sensorMode').classList.toggle('active', mode === 'sensor'); document.getElementById('sensorMode').classList.toggle('active', mode === 'sensor');
document.getElementById('aircraftMode')?.classList.toggle('active', mode === 'aircraft'); document.getElementById('aircraftMode')?.classList.toggle('active', mode === 'aircraft');
document.getElementById('satelliteMode').classList.toggle('active', mode === 'satellite'); document.getElementById('satelliteMode').classList.toggle('active', mode === 'satellite');
document.getElementById('wifiMode').classList.toggle('active', mode === 'wifi'); document.getElementById('wifiMode').classList.toggle('active', mode === 'wifi');
document.getElementById('bluetoothMode').classList.toggle('active', mode === 'bluetooth'); document.getElementById('bluetoothMode').classList.toggle('active', mode === 'bluetooth');
document.getElementById('listeningPostMode').classList.toggle('active', mode === 'listening'); document.getElementById('listeningPostMode').classList.toggle('active', mode === 'listening');
document.getElementById('aprsMode')?.classList.toggle('active', mode === 'aprs'); document.getElementById('aprsMode')?.classList.toggle('active', mode === 'aprs');
document.getElementById('tscmMode')?.classList.toggle('active', mode === 'tscm'); document.getElementById('tscmMode')?.classList.toggle('active', mode === 'tscm');
document.getElementById('rtlamrMode')?.classList.toggle('active', mode === 'rtlamr'); document.getElementById('rtlamrMode')?.classList.toggle('active', mode === 'rtlamr');
document.getElementById('spystationsMode')?.classList.toggle('active', mode === 'spystations'); document.getElementById('spystationsMode')?.classList.toggle('active', mode === 'spystations');
document.getElementById('meshtasticMode')?.classList.toggle('active', mode === 'meshtastic'); document.getElementById('meshtasticMode')?.classList.toggle('active', mode === 'meshtastic');
// Toggle stats visibility // Toggle stats visibility
document.getElementById('pagerStats').style.display = mode === 'pager' ? 'flex' : 'none'; document.getElementById('pagerStats').style.display = mode === 'pager' ? 'flex' : 'none';
document.getElementById('sensorStats').style.display = mode === 'sensor' ? 'flex' : 'none'; document.getElementById('sensorStats').style.display = mode === 'sensor' ? 'flex' : 'none';
document.getElementById('aircraftStats').style.display = mode === 'aircraft' ? 'flex' : 'none'; document.getElementById('aircraftStats').style.display = mode === 'aircraft' ? 'flex' : 'none';
document.getElementById('satelliteStats').style.display = mode === 'satellite' ? 'flex' : 'none'; document.getElementById('satelliteStats').style.display = mode === 'satellite' ? 'flex' : 'none';
document.getElementById('wifiStats').style.display = mode === 'wifi' ? 'flex' : 'none'; document.getElementById('wifiStats').style.display = mode === 'wifi' ? 'flex' : 'none';
// Hide signal meter - individual panels show signal strength where needed // Hide signal meter - individual panels show signal strength where needed
document.getElementById('signalMeter').style.display = 'none'; document.getElementById('signalMeter').style.display = 'none';
// Show/hide dashboard buttons in nav bar // Show/hide dashboard buttons in nav bar
document.getElementById('adsbDashboardBtn').style.display = mode === 'aircraft' ? 'inline-flex' : 'none'; document.getElementById('adsbDashboardBtn').style.display = mode === 'aircraft' ? 'inline-flex' : 'none';
document.getElementById('satelliteDashboardBtn').style.display = mode === 'satellite' ? 'inline-flex' : 'none'; document.getElementById('satelliteDashboardBtn').style.display = mode === 'satellite' ? 'inline-flex' : 'none';
// Update active mode indicator // Update active mode indicator
const modeNames = { const modeNames = {
'pager': 'PAGER', 'pager': 'PAGER',
'sensor': '433MHZ', 'sensor': '433MHZ',
'aircraft': 'AIRCRAFT', 'aircraft': 'AIRCRAFT',
'satellite': 'SATELLITE', 'satellite': 'SATELLITE',
'wifi': 'WIFI', 'wifi': 'WIFI',
'bluetooth': 'BLUETOOTH', 'bluetooth': 'BLUETOOTH',
'listening': 'LISTENING POST', 'listening': 'LISTENING POST',
'tscm': 'TSCM', 'tscm': 'TSCM',
'aprs': 'APRS', 'aprs': 'APRS',
'meshtastic': 'MESHTASTIC' 'meshtastic': 'MESHTASTIC'
}; };
document.getElementById('activeModeIndicator').innerHTML = '<span class="pulse-dot"></span>' + modeNames[mode]; document.getElementById('activeModeIndicator').innerHTML = '<span class="pulse-dot"></span>' + modeNames[mode];
// Update mobile nav buttons // Update mobile nav buttons
updateMobileNavButtons(mode); updateMobileNavButtons(mode);
// Close mobile drawer when mode is switched (on mobile) // Close mobile drawer when mode is switched (on mobile)
if (window.innerWidth < 1024 && typeof window.closeMobileDrawer === 'function') { if (window.innerWidth < 1024 && typeof window.closeMobileDrawer === 'function') {
window.closeMobileDrawer(); window.closeMobileDrawer();
} }
// Toggle layout containers // Toggle layout containers
document.getElementById('wifiLayoutContainer').style.display = mode === 'wifi' ? 'flex' : 'none'; document.getElementById('wifiLayoutContainer').style.display = mode === 'wifi' ? 'flex' : 'none';
document.getElementById('btLayoutContainer').style.display = mode === 'bluetooth' ? 'flex' : 'none'; document.getElementById('btLayoutContainer').style.display = mode === 'bluetooth' ? 'flex' : 'none';
// Respect the "Show Radar Display" checkbox for aircraft mode // Respect the "Show Radar Display" checkbox for aircraft mode
const showRadar = document.getElementById('adsbEnableMap')?.checked; const showRadar = document.getElementById('adsbEnableMap')?.checked;
document.getElementById('aircraftVisuals').style.display = (mode === 'aircraft' && showRadar) ? 'grid' : 'none'; document.getElementById('aircraftVisuals').style.display = (mode === 'aircraft' && showRadar) ? 'grid' : 'none';
document.getElementById('satelliteVisuals').style.display = mode === 'satellite' ? 'block' : 'none'; document.getElementById('satelliteVisuals').style.display = mode === 'satellite' ? 'block' : 'none';
document.getElementById('listeningPostVisuals').style.display = mode === 'listening' ? 'grid' : 'none'; document.getElementById('listeningPostVisuals').style.display = mode === 'listening' ? 'grid' : 'none';
// Update output panel title based on mode // Update output panel title based on mode
const titles = { const titles = {
'pager': 'Pager Decoder', 'pager': 'Pager Decoder',
'sensor': '433MHz Sensor Monitor', 'sensor': '433MHz Sensor Monitor',
'aircraft': 'ADS-B Aircraft Tracker', 'aircraft': 'ADS-B Aircraft Tracker',
'satellite': 'Satellite Monitor', 'satellite': 'Satellite Monitor',
'wifi': 'WiFi Scanner', 'wifi': 'WiFi Scanner',
'bluetooth': 'Bluetooth Scanner', 'bluetooth': 'Bluetooth Scanner',
'listening': 'Listening Post', 'listening': 'Listening Post',
'meshtastic': 'Meshtastic Mesh Monitor' 'meshtastic': 'Meshtastic Mesh Monitor'
}; };
document.getElementById('outputTitle').textContent = titles[mode] || 'Signal Monitor'; document.getElementById('outputTitle').textContent = titles[mode] || 'Signal Monitor';
// Show/hide Device Intelligence for modes that use it // Show/hide Device Intelligence for modes that use it
const reconBtn = document.getElementById('reconBtn'); const reconBtn = document.getElementById('reconBtn');
const intelBtn = document.querySelector('[onclick="exportDeviceDB()"]'); const intelBtn = document.querySelector('[onclick="exportDeviceDB()"]');
if (mode === 'satellite' || mode === 'aircraft' || mode === 'listening') { if (mode === 'satellite' || mode === 'aircraft' || mode === 'listening') {
document.getElementById('reconPanel').style.display = 'none'; document.getElementById('reconPanel').style.display = 'none';
if (reconBtn) reconBtn.style.display = 'none'; if (reconBtn) reconBtn.style.display = 'none';
if (intelBtn) intelBtn.style.display = 'none'; if (intelBtn) intelBtn.style.display = 'none';
} else { } else {
if (reconBtn) reconBtn.style.display = 'inline-block'; if (reconBtn) reconBtn.style.display = 'inline-block';
if (intelBtn) intelBtn.style.display = 'inline-block'; if (intelBtn) intelBtn.style.display = 'inline-block';
if (typeof reconEnabled !== 'undefined' && reconEnabled) { if (typeof reconEnabled !== 'undefined' && reconEnabled) {
document.getElementById('reconPanel').style.display = 'block'; document.getElementById('reconPanel').style.display = 'block';
} }
} }
// Show RTL-SDR device section for modes that use it // Show RTL-SDR device section for modes that use it
document.getElementById('rtlDeviceSection').style.display = document.getElementById('rtlDeviceSection').style.display =
(mode === 'pager' || mode === 'sensor' || mode === 'aircraft' || mode === 'listening') ? 'block' : 'none'; (mode === 'pager' || mode === 'sensor' || mode === 'aircraft' || mode === 'listening') ? 'block' : 'none';
// Toggle mode-specific tool status displays // Toggle mode-specific tool status displays
document.getElementById('toolStatusPager').style.display = (mode === 'pager') ? 'grid' : 'none'; document.getElementById('toolStatusPager').style.display = (mode === 'pager') ? 'grid' : 'none';
document.getElementById('toolStatusSensor').style.display = (mode === 'sensor') ? 'grid' : 'none'; document.getElementById('toolStatusSensor').style.display = (mode === 'sensor') ? 'grid' : 'none';
document.getElementById('toolStatusAircraft').style.display = (mode === 'aircraft') ? 'grid' : 'none'; document.getElementById('toolStatusAircraft').style.display = (mode === 'aircraft') ? 'grid' : 'none';
// Hide waterfall and output console for modes with their own visualizations // Hide waterfall and output console for modes with their own visualizations
document.querySelector('.waterfall-container').style.display = document.querySelector('.waterfall-container').style.display =
(mode === 'satellite' || mode === 'listening' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'meshtastic' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations') ? 'none' : 'block'; (mode === 'satellite' || mode === 'listening' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'meshtastic' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations') ? 'none' : 'block';
document.getElementById('output').style.display = document.getElementById('output').style.display =
(mode === 'satellite' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'meshtastic' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations') ? 'none' : 'block'; (mode === 'satellite' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'meshtastic' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations') ? 'none' : 'block';
document.querySelector('.status-bar').style.display = (mode === 'satellite' || mode === 'tscm' || mode === 'meshtastic' || mode === 'aprs' || mode === 'spystations') ? 'none' : 'flex'; document.querySelector('.status-bar').style.display = (mode === 'satellite' || mode === 'tscm' || mode === 'meshtastic' || mode === 'aprs' || mode === 'spystations') ? 'none' : 'flex';
// Load interfaces and initialize visualizations when switching modes // Load interfaces and initialize visualizations when switching modes
if (mode === 'wifi') { if (mode === 'wifi') {
if (typeof refreshWifiInterfaces === 'function') refreshWifiInterfaces(); if (typeof refreshWifiInterfaces === 'function') refreshWifiInterfaces();
if (typeof initRadar === 'function') initRadar(); if (typeof initRadar === 'function') initRadar();
if (typeof initWatchList === 'function') initWatchList(); if (typeof initWatchList === 'function') initWatchList();
} else if (mode === 'bluetooth') { } else if (mode === 'bluetooth') {
if (typeof refreshBtInterfaces === 'function') refreshBtInterfaces(); if (typeof refreshBtInterfaces === 'function') refreshBtInterfaces();
if (typeof initBtRadar === 'function') initBtRadar(); if (typeof initBtRadar === 'function') initBtRadar();
} else if (mode === 'aircraft') { } else if (mode === 'aircraft') {
if (typeof checkAdsbTools === 'function') checkAdsbTools(); if (typeof checkAdsbTools === 'function') checkAdsbTools();
if (typeof initAircraftRadar === 'function') initAircraftRadar(); if (typeof initAircraftRadar === 'function') initAircraftRadar();
} else if (mode === 'satellite') { } else if (mode === 'satellite') {
if (typeof initPolarPlot === 'function') initPolarPlot(); if (typeof initPolarPlot === 'function') initPolarPlot();
if (typeof initSatelliteList === 'function') initSatelliteList(); if (typeof initSatelliteList === 'function') initSatelliteList();
} else if (mode === 'listening') { } else if (mode === 'listening') {
if (typeof checkScannerTools === 'function') checkScannerTools(); if (typeof checkScannerTools === 'function') checkScannerTools();
if (typeof checkAudioTools === 'function') checkAudioTools(); if (typeof checkAudioTools === 'function') checkAudioTools();
if (typeof populateScannerDeviceSelect === 'function') populateScannerDeviceSelect(); if (typeof populateScannerDeviceSelect === 'function') populateScannerDeviceSelect();
if (typeof populateAudioDeviceSelect === 'function') populateAudioDeviceSelect(); if (typeof populateAudioDeviceSelect === 'function') populateAudioDeviceSelect();
} else if (mode === 'meshtastic') { } else if (mode === 'meshtastic') {
if (typeof Meshtastic !== 'undefined' && Meshtastic.init) Meshtastic.init(); if (typeof Meshtastic !== 'undefined' && Meshtastic.init) Meshtastic.init();
} }
} }
// ============== SECTION COLLAPSE ============== // ============== SECTION COLLAPSE ==============
function toggleSection(el) { function toggleSection(el) {
el.closest('.section').classList.toggle('collapsed'); el.closest('.section').classList.toggle('collapsed');
} }
// ============== THEME MANAGEMENT ============== // ============== THEME MANAGEMENT ==============
function toggleTheme() { function toggleTheme() {
const html = document.documentElement; const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme'); const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'light' ? 'dark' : 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light';
html.setAttribute('data-theme', newTheme); html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme); localStorage.setItem('theme', newTheme);
// Update button text // Update button text
const btn = document.getElementById('themeToggle'); const btn = document.getElementById('themeToggle');
if (btn) { if (btn) {
btn.textContent = newTheme === 'light' ? '🌙' : '☀️'; btn.textContent = newTheme === 'light' ? '🌙' : '☀️';
} }
} }
function loadTheme() { function loadTheme() {
const savedTheme = localStorage.getItem('theme') || 'dark'; const savedTheme = localStorage.getItem('theme') || 'dark';
document.documentElement.setAttribute('data-theme', savedTheme); document.documentElement.setAttribute('data-theme', savedTheme);
const btn = document.getElementById('themeToggle'); const btn = document.getElementById('themeToggle');
if (btn) { if (btn) {
btn.textContent = savedTheme === 'light' ? '🌙' : '☀️'; btn.textContent = savedTheme === 'light' ? '🌙' : '☀️';
} }
} }
// ============== AUTO-SCROLL ============== // ============== AUTO-SCROLL ==============
function toggleAutoScroll() { function toggleAutoScroll() {
autoScroll = !autoScroll; autoScroll = !autoScroll;
localStorage.setItem('autoScroll', autoScroll); localStorage.setItem('autoScroll', autoScroll);
updateAutoScrollButton(); updateAutoScrollButton();
} }
function updateAutoScrollButton() { function updateAutoScrollButton() {
const btn = document.getElementById('autoScrollBtn'); const btn = document.getElementById('autoScrollBtn');
if (btn) { if (btn) {
btn.innerHTML = autoScroll ? '⬇ AUTO-SCROLL ON' : '⬇ AUTO-SCROLL OFF'; btn.innerHTML = autoScroll ? '⬇ AUTO-SCROLL ON' : '⬇ AUTO-SCROLL OFF';
btn.classList.toggle('active', autoScroll); btn.classList.toggle('active', autoScroll);
} }
} }
// ============== SDR DEVICE MANAGEMENT ============== // ============== SDR DEVICE MANAGEMENT ==============
function getSelectedDevice() { function getSelectedDevice() {
return document.getElementById('deviceSelect').value; return document.getElementById('deviceSelect').value;
} }
function getSelectedSDRType() { function getSelectedSDRType() {
return document.getElementById('sdrTypeSelect').value; return document.getElementById('sdrTypeSelect').value;
} }
function reserveDevice(deviceIndex, modeId) { function reserveDevice(deviceIndex, modeId) {
sdrDeviceUsage[modeId] = deviceIndex; sdrDeviceUsage[modeId] = deviceIndex;
} }
function releaseDevice(modeId) { function releaseDevice(modeId) {
delete sdrDeviceUsage[modeId]; delete sdrDeviceUsage[modeId];
} }
function checkDeviceAvailability(requestingMode) { function checkDeviceAvailability(requestingMode) {
const selectedDevice = parseInt(getSelectedDevice()); const selectedDevice = parseInt(getSelectedDevice());
for (const [mode, device] of Object.entries(sdrDeviceUsage)) { for (const [mode, device] of Object.entries(sdrDeviceUsage)) {
if (mode !== requestingMode && device === selectedDevice) { if (mode !== requestingMode && device === selectedDevice) {
alert(`Device ${selectedDevice} is currently in use by ${mode} mode. Please select a different device or stop the other scan first.`); alert(`Device ${selectedDevice} is currently in use by ${mode} mode. Please select a different device or stop the other scan first.`);
return false; return false;
} }
} }
return true; return true;
} }
// ============== BIAS-T SETTINGS ============== // ============== BIAS-T SETTINGS ==============
function saveBiasTSetting() { function saveBiasTSetting() {
const enabled = document.getElementById('biasT')?.checked || false; const enabled = document.getElementById('biasT')?.checked || false;
localStorage.setItem('biasTEnabled', enabled); localStorage.setItem('biasTEnabled', enabled);
} }
function getBiasTEnabled() { function getBiasTEnabled() {
return document.getElementById('biasT')?.checked || false; return document.getElementById('biasT')?.checked || false;
} }
function loadBiasTSetting() { function loadBiasTSetting() {
const saved = localStorage.getItem('biasTEnabled'); const saved = localStorage.getItem('biasTEnabled');
if (saved === 'true') { if (saved === 'true') {
const checkbox = document.getElementById('biasT'); const checkbox = document.getElementById('biasT');
if (checkbox) checkbox.checked = true; if (checkbox) checkbox.checked = true;
} }
} }
// ============== REMOTE SDR ============== // ============== REMOTE SDR ==============
function toggleRemoteSDR() { function toggleRemoteSDR() {
const useRemote = document.getElementById('useRemoteSDR').checked; const useRemote = document.getElementById('useRemoteSDR').checked;
const configDiv = document.getElementById('remoteSDRConfig'); const configDiv = document.getElementById('remoteSDRConfig');
const localControls = document.querySelectorAll('#sdrTypeSelect, #deviceSelect'); const localControls = document.querySelectorAll('#sdrTypeSelect, #deviceSelect');
if (useRemote) { if (useRemote) {
configDiv.style.display = 'block'; configDiv.style.display = 'block';
localControls.forEach(el => el.disabled = true); localControls.forEach(el => el.disabled = true);
} else { } else {
configDiv.style.display = 'none'; configDiv.style.display = 'none';
localControls.forEach(el => el.disabled = false); localControls.forEach(el => el.disabled = false);
} }
} }
function getRemoteSDRConfig() { function getRemoteSDRConfig() {
const useRemote = document.getElementById('useRemoteSDR')?.checked; const useRemote = document.getElementById('useRemoteSDR')?.checked;
if (!useRemote) return null; if (!useRemote) return null;
const host = document.getElementById('rtlTcpHost')?.value || 'localhost'; const host = document.getElementById('rtlTcpHost')?.value || 'localhost';
const port = parseInt(document.getElementById('rtlTcpPort')?.value || '1234'); const port = parseInt(document.getElementById('rtlTcpPort')?.value || '1234');
if (!host || isNaN(port)) { if (!host || isNaN(port)) {
alert('Please enter valid rtl_tcp host and port'); alert('Please enter valid rtl_tcp host and port');
return false; return false;
} }
return { host, port }; return { host, port };
} }
// ============== OUTPUT DISPLAY ============== // ============== OUTPUT DISPLAY ==============
function showInfo(text) { function showInfo(text) {
const output = document.getElementById('output'); const output = document.getElementById('output');
if (!output) return; if (!output) return;
const placeholder = output.querySelector('.placeholder'); const placeholder = output.querySelector('.placeholder');
if (placeholder) placeholder.remove(); if (placeholder) placeholder.remove();
const infoEl = document.createElement('div'); const infoEl = document.createElement('div');
infoEl.className = 'info-msg'; infoEl.className = 'info-msg';
infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #888; word-break: break-all;'; infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "Terminus", monospace; font-size: 11px; color: #888; word-break: break-all;';
infoEl.textContent = text; infoEl.textContent = text;
output.insertBefore(infoEl, output.firstChild); output.insertBefore(infoEl, output.firstChild);
} }
function showError(text) { function showError(text) {
const output = document.getElementById('output'); const output = document.getElementById('output');
if (!output) return; if (!output) return;
const placeholder = output.querySelector('.placeholder'); const placeholder = output.querySelector('.placeholder');
if (placeholder) placeholder.remove(); if (placeholder) placeholder.remove();
const errorEl = document.createElement('div'); const errorEl = document.createElement('div');
errorEl.className = 'error-msg'; errorEl.className = 'error-msg';
errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #ff6688; word-break: break-all;'; errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "Terminus", monospace; font-size: 11px; color: #ff6688; word-break: break-all;';
errorEl.textContent = '⚠ ' + text; errorEl.textContent = '⚠ ' + text;
output.insertBefore(errorEl, output.firstChild); output.insertBefore(errorEl, output.firstChild);
} }
// ============== INITIALIZATION ============== // ============== INITIALIZATION ==============
// ============== MOBILE NAVIGATION ============== // ============== MOBILE NAVIGATION ==============
function initMobileNav() { function initMobileNav() {
const hamburgerBtn = document.getElementById('hamburgerBtn'); const hamburgerBtn = document.getElementById('hamburgerBtn');
const sidebar = document.getElementById('mainSidebar'); const sidebar = document.getElementById('mainSidebar');
const overlay = document.getElementById('drawerOverlay'); const overlay = document.getElementById('drawerOverlay');
if (!hamburgerBtn || !sidebar || !overlay) return; if (!hamburgerBtn || !sidebar || !overlay) return;
function openDrawer() { function openDrawer() {
sidebar.classList.add('open'); sidebar.classList.add('open');
overlay.classList.add('visible'); overlay.classList.add('visible');
hamburgerBtn.classList.add('active'); hamburgerBtn.classList.add('active');
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
} }
function closeDrawer() { function closeDrawer() {
sidebar.classList.remove('open'); sidebar.classList.remove('open');
overlay.classList.remove('visible'); overlay.classList.remove('visible');
hamburgerBtn.classList.remove('active'); hamburgerBtn.classList.remove('active');
document.body.style.overflow = ''; document.body.style.overflow = '';
} }
function toggleDrawer() { function toggleDrawer() {
if (sidebar.classList.contains('open')) { if (sidebar.classList.contains('open')) {
closeDrawer(); closeDrawer();
} else { } else {
openDrawer(); openDrawer();
} }
} }
hamburgerBtn.addEventListener('click', toggleDrawer); hamburgerBtn.addEventListener('click', toggleDrawer);
overlay.addEventListener('click', closeDrawer); overlay.addEventListener('click', closeDrawer);
// Close drawer when resizing to desktop // Close drawer when resizing to desktop
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
if (window.innerWidth >= 1024) { if (window.innerWidth >= 1024) {
closeDrawer(); closeDrawer();
} }
}); });
// Expose for external use // Expose for external use
window.toggleMobileDrawer = toggleDrawer; window.toggleMobileDrawer = toggleDrawer;
window.closeMobileDrawer = closeDrawer; window.closeMobileDrawer = closeDrawer;
} }
function setViewportHeight() { function setViewportHeight() {
// Fix for iOS Safari address bar height // Fix for iOS Safari address bar height
const vh = window.innerHeight * 0.01; const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`); document.documentElement.style.setProperty('--vh', `${vh}px`);
} }
function updateMobileNavButtons(mode) { function updateMobileNavButtons(mode) {
// Update mobile nav bar buttons // Update mobile nav bar buttons
document.querySelectorAll('.mobile-nav-btn').forEach(btn => { document.querySelectorAll('.mobile-nav-btn').forEach(btn => {
const btnMode = btn.getAttribute('data-mode'); const btnMode = btn.getAttribute('data-mode');
btn.classList.toggle('active', btnMode === mode); btn.classList.toggle('active', btnMode === mode);
}); });
} }
function initApp() { function initApp() {
// Check disclaimer // Check disclaimer
checkDisclaimer(); checkDisclaimer();
// Load theme // Load theme
loadTheme(); loadTheme();
// Start clock // Start clock
updateHeaderClock(); updateHeaderClock();
setInterval(updateHeaderClock, 1000); setInterval(updateHeaderClock, 1000);
// Load bias-T setting // Load bias-T setting
loadBiasTSetting(); loadBiasTSetting();
// Initialize observer location inputs // Initialize observer location inputs
const adsbLatInput = document.getElementById('adsbObsLat'); const adsbLatInput = document.getElementById('adsbObsLat');
const adsbLonInput = document.getElementById('adsbObsLon'); const adsbLonInput = document.getElementById('adsbObsLon');
const obsLatInput = document.getElementById('obsLat'); const obsLatInput = document.getElementById('obsLat');
const obsLonInput = document.getElementById('obsLon'); const obsLonInput = document.getElementById('obsLon');
if (adsbLatInput) adsbLatInput.value = observerLocation.lat.toFixed(4); if (adsbLatInput) adsbLatInput.value = observerLocation.lat.toFixed(4);
if (adsbLonInput) adsbLonInput.value = observerLocation.lon.toFixed(4); if (adsbLonInput) adsbLonInput.value = observerLocation.lon.toFixed(4);
if (obsLatInput) obsLatInput.value = observerLocation.lat.toFixed(4); if (obsLatInput) obsLatInput.value = observerLocation.lat.toFixed(4);
if (obsLonInput) obsLonInput.value = observerLocation.lon.toFixed(4); if (obsLonInput) obsLonInput.value = observerLocation.lon.toFixed(4);
// Update UI state // Update UI state
updateAutoScrollButton(); updateAutoScrollButton();
// Make sections collapsible // Make sections collapsible
document.querySelectorAll('.section h3').forEach(h3 => { document.querySelectorAll('.section h3').forEach(h3 => {
h3.addEventListener('click', function() { h3.addEventListener('click', function() {
this.parentElement.classList.toggle('collapsed'); this.parentElement.classList.toggle('collapsed');
}); });
}); });
// Collapse all sections by default (except SDR Device which is first) // Collapse all sections by default (except SDR Device which is first)
document.querySelectorAll('.section').forEach((section, index) => { document.querySelectorAll('.section').forEach((section, index) => {
if (index > 0) { if (index > 0) {
section.classList.add('collapsed'); section.classList.add('collapsed');
} }
}); });
// Initialize mobile navigation // Initialize mobile navigation
initMobileNav(); initMobileNav();
// Set viewport height for mobile browsers // Set viewport height for mobile browsers
setViewportHeight(); setViewportHeight();
window.addEventListener('resize', setViewportHeight); window.addEventListener('resize', setViewportHeight);
} }
// Run initialization when DOM is ready // Run initialization when DOM is ready
document.addEventListener('DOMContentLoaded', initApp); document.addEventListener('DOMContentLoaded', initApp);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -84,7 +84,7 @@ const SpyStations = (function() {
modeContainer.innerHTML = modes.map(m => ` modeContainer.innerHTML = modes.map(m => `
<label class="inline-checkbox"> <label class="inline-checkbox">
<input type="checkbox" data-mode="${m}" checked onchange="SpyStations.applyFilters()"> <input type="checkbox" data-mode="${m}" checked onchange="SpyStations.applyFilters()">
<span style="font-family: 'JetBrains Mono', monospace; font-size: 10px;">${m}</span> <span style="font-family: 'Terminus', monospace; font-size: 10px;">${m}</span>
</label> </label>
`).join(''); `).join('');
} }
Binary file not shown.
+4895 -4895
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -466,7 +466,7 @@
if (!points.length) { if (!points.length) {
ctx.fillStyle = 'rgba(156, 163, 175, 0.6)'; ctx.fillStyle = 'rgba(156, 163, 175, 0.6)';
ctx.font = '12px "JetBrains Mono", monospace'; ctx.font = '12px "Terminus", monospace';
ctx.fillText(`No ${label.toLowerCase()} data`, 12, height / 2); ctx.fillText(`No ${label.toLowerCase()} data`, 12, height / 2);
return; return;
} }
@@ -474,7 +474,7 @@
const series = points.map(p => p[field]).filter(v => v !== null && v !== undefined); const series = points.map(p => p[field]).filter(v => v !== null && v !== undefined);
if (!series.length) { if (!series.length) {
ctx.fillStyle = 'rgba(156, 163, 175, 0.6)'; ctx.fillStyle = 'rgba(156, 163, 175, 0.6)';
ctx.font = '12px "JetBrains Mono", monospace'; ctx.font = '12px "Terminus", monospace';
ctx.fillText(`No ${label.toLowerCase()} data`, 12, height / 2); ctx.fillText(`No ${label.toLowerCase()} data`, 12, height / 2);
return; return;
} }
@@ -515,7 +515,7 @@
} }
ctx.fillStyle = 'rgba(226, 232, 240, 0.8)'; ctx.fillStyle = 'rgba(226, 232, 240, 0.8)';
ctx.font = '11px "JetBrains Mono", monospace'; ctx.font = '11px "Terminus", monospace';
ctx.fillText(`${maxVal} ${unit}`, 12, padding); ctx.fillText(`${maxVal} ${unit}`, 12, padding);
ctx.fillText(`${minVal} ${unit}`, 12, height - padding); ctx.fillText(`${minVal} ${unit}`, 12, height - padding);
} }
+583 -583
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1499,7 +1499,7 @@
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
font-size: 11px; font-size: 11px;
font-family: 'JetBrains Mono', monospace; font-family: var(--font-mono);
cursor: pointer; cursor: pointer;
} }
.agent-select-sm:focus { .agent-select-sm:focus {
+14 -14
View File
@@ -290,7 +290,7 @@
╚═════╝ ╚══════╝╚═╝ ╚═══╝╚═╝╚══════╝╚═════╝</pre> ╚═════╝ ╚══════╝╚═╝ ╚═══╝╚═╝╚══════╝╚═════╝</pre>
<div style="margin: 25px 0; padding: 15px; background: #0a0a0a; border-left: 3px solid var(--accent-red);"> <div style="margin: 25px 0; padding: 15px; background: #0a0a0a; border-left: 3px solid var(--accent-red);">
<p <p
style="font-family: 'JetBrains Mono', monospace; font-size: 11px; color: #888; text-align: left; margin: 0;"> style="font-family: var(--font-mono); font-size: 11px; color: #888; text-align: left; margin: 0;">
<span style="color: var(--accent-red);">root@intercepted:</span><span <span style="color: var(--accent-red);">root@intercepted:</span><span
style="color: var(--accent-cyan);">~#</span> sudo access --grant-permission<br> style="color: var(--accent-cyan);">~#</span> sudo access --grant-permission<br>
<span style="color: #666;">[sudo] password for user: ********</span><br> <span style="color: #666;">[sudo] password for user: ********</span><br>
@@ -989,7 +989,7 @@
style="color: var(--accent-orange); text-shadow: 0 0 10px var(--accent-orange); margin-bottom: 8px;"> style="color: var(--accent-orange); text-shadow: 0 0 10px var(--accent-orange); margin-bottom: 8px;">
PACKET LOG</h5> PACKET LOG</h5>
<div id="aprsPacketLog" <div id="aprsPacketLog"
style="flex: 1; overflow-y: auto; font-family: 'JetBrains Mono', monospace; font-size: 10px; background: rgba(0,0,0,0.3); padding: 8px; border-radius: 4px;"> style="flex: 1; overflow-y: auto; font-family: var(--font-mono); font-size: 10px; background: rgba(0,0,0,0.3); padding: 8px; border-radius: 4px;">
<div style="color: var(--text-muted);">Waiting for packets...</div> <div style="color: var(--text-muted);">Waiting for packets...</div>
</div> </div>
</div> </div>
@@ -1009,7 +1009,7 @@
STOPPED</div> STOPPED</div>
<div style="display: flex; justify-content: center; align-items: baseline; gap: 8px;"> <div style="display: flex; justify-content: center; align-items: baseline; gap: 8px;">
<div class="freq-digits" id="mainScannerFreq" <div class="freq-digits" id="mainScannerFreq"
style="font-size: 52px; font-weight: bold; color: var(--accent-cyan); text-shadow: 0 0 30px var(--accent-cyan); font-family: 'JetBrains Mono', monospace; letter-spacing: 3px;"> style="font-size: 52px; font-weight: bold; color: var(--accent-cyan); text-shadow: 0 0 30px var(--accent-cyan); font-family: var(--font-mono); letter-spacing: 3px;">
118.000</div> 118.000</div>
<span class="freq-unit" <span class="freq-unit"
style="font-size: 20px; color: var(--text-secondary); font-weight: 500;">MHz</span> style="font-size: 20px; color: var(--text-secondary); font-weight: 500;">MHz</span>
@@ -1237,7 +1237,7 @@
START</div> START</div>
<input type="number" id="radioScanStart" value="118" step="0.1" <input type="number" id="radioScanStart" value="118" step="0.1"
class="radio-input" class="radio-input"
style="width: 100%; font-size: 16px; padding: 8px 6px; text-align: center; font-family: 'JetBrains Mono', monospace; font-weight: bold; color: var(--accent-cyan);"> style="width: 100%; font-size: 16px; padding: 8px 6px; text-align: center; font-family: var(--font-mono); font-weight: bold; color: var(--accent-cyan);">
</div> </div>
<span <span
style="color: var(--text-muted); font-size: 16px; padding-top: 12px;">→</span> style="color: var(--text-muted); font-size: 16px; padding-top: 12px;">→</span>
@@ -1246,7 +1246,7 @@
END</div> END</div>
<input type="number" id="radioScanEnd" value="137" step="0.1" <input type="number" id="radioScanEnd" value="137" step="0.1"
class="radio-input" class="radio-input"
style="width: 100%; font-size: 16px; padding: 8px 6px; text-align: center; font-family: 'JetBrains Mono', monospace; font-weight: bold; color: var(--accent-cyan);"> style="width: 100%; font-size: 16px; padding: 8px 6px; text-align: center; font-family: var(--font-mono); font-weight: bold; color: var(--accent-cyan);">
</div> </div>
</div> </div>
</div> </div>
@@ -1312,19 +1312,19 @@
<div style="display: flex; align-items: center; justify-content: space-between;"> <div style="display: flex; align-items: center; justify-content: space-between;">
<span style="font-size: 9px; color: var(--text-muted);">SIGNALS</span> <span style="font-size: 9px; color: var(--text-muted);">SIGNALS</span>
<span <span
style="color: var(--accent-green); font-size: 18px; font-weight: bold; font-family: 'JetBrains Mono', monospace;" style="color: var(--accent-green); font-size: 18px; font-weight: bold; font-family: var(--font-mono);"
id="mainSignalCount">0</span> id="mainSignalCount">0</span>
</div> </div>
<div style="display: flex; align-items: center; justify-content: space-between;"> <div style="display: flex; align-items: center; justify-content: space-between;">
<span style="font-size: 9px; color: var(--text-muted);">SCANNED</span> <span style="font-size: 9px; color: var(--text-muted);">SCANNED</span>
<span <span
style="color: var(--accent-cyan); font-size: 18px; font-weight: bold; font-family: 'JetBrains Mono', monospace;" style="color: var(--accent-cyan); font-size: 18px; font-weight: bold; font-family: var(--font-mono);"
id="mainFreqsScanned">0</span> id="mainFreqsScanned">0</span>
</div> </div>
<div style="display: flex; align-items: center; justify-content: space-between;"> <div style="display: flex; align-items: center; justify-content: space-between;">
<span style="font-size: 9px; color: var(--text-muted);">CYCLES</span> <span style="font-size: 9px; color: var(--text-muted);">CYCLES</span>
<span <span
style="color: var(--accent-orange); font-size: 18px; font-weight: bold; font-family: 'JetBrains Mono', monospace;" style="color: var(--accent-orange); font-size: 18px; font-weight: bold; font-family: var(--font-mono);"
id="mainScanCycles">0</span> id="mainScanCycles">0</span>
</div> </div>
</div> </div>
@@ -4223,7 +4223,7 @@
const infoEl = document.createElement('div'); const infoEl = document.createElement('div');
infoEl.className = 'info-msg'; infoEl.className = 'info-msg';
infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #888; word-break: break-all;'; infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "Terminus", monospace; font-size: 11px; color: #888; word-break: break-all;';
infoEl.textContent = text; infoEl.textContent = text;
output.insertBefore(infoEl, output.firstChild); output.insertBefore(infoEl, output.firstChild);
} }
@@ -4239,7 +4239,7 @@
const errorEl = document.createElement('div'); const errorEl = document.createElement('div');
errorEl.className = 'error-msg'; errorEl.className = 'error-msg';
errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #ff6688; word-break: break-all;'; errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "Terminus", monospace; font-size: 11px; color: #ff6688; word-break: break-all;';
errorEl.textContent = '⚠ ' + text; errorEl.textContent = '⚠ ' + text;
output.insertBefore(errorEl, output.firstChild); output.insertBefore(errorEl, output.firstChild);
} }
@@ -6855,7 +6855,7 @@
// Draw total in center // Draw total in center
ctx.fillStyle = '#fff'; ctx.fillStyle = '#fff';
ctx.font = 'bold 16px JetBrains Mono'; ctx.font = 'bold 16px Terminus';
ctx.textAlign = 'center'; ctx.textAlign = 'center';
ctx.textBaseline = 'middle'; ctx.textBaseline = 'middle';
ctx.fillText(total, cx, cy); ctx.fillText(total, cx, cy);
@@ -8625,7 +8625,7 @@
// Label // Label
if (el > 0) { if (el > 0) {
ctx.fillStyle = '#444'; ctx.fillStyle = '#444';
ctx.font = '10px JetBrains Mono'; ctx.font = '10px Terminus';
ctx.textAlign = 'center'; ctx.textAlign = 'center';
ctx.fillText(el + '°', cx, cy - r + 12); ctx.fillText(el + '°', cx, cy - r + 12);
} }
@@ -8698,7 +8698,7 @@
// Label // Label
ctx.fillStyle = '#fff'; ctx.fillStyle = '#fff';
ctx.font = '11px JetBrains Mono'; ctx.font = '11px Terminus';
ctx.fillText(pass.satellite, maxX + 10, maxY - 5); ctx.fillText(pass.satellite, maxX + 10, maxY - 5);
} }
} }
@@ -12145,7 +12145,7 @@
<textarea id="tleInput" placeholder="ISS (ZARYA) <textarea id="tleInput" placeholder="ISS (ZARYA)
1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002 1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
2 25544 51.6400 208.9163 0006703 296.5855 63.4606 15.49995465478450" 2 25544 51.6400 208.9163 0006703 296.5855 63.4606 15.49995465478450"
style="width: 100%; height: 150px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; font-family: 'JetBrains Mono', monospace; font-size: 11px; resize: vertical;"></textarea> style="width: 100%; height: 150px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; font-family: var(--font-mono); font-size: 11px; resize: vertical;"></textarea>
<button class="preset-btn" onclick="addFromTLE()" style="margin-top: 10px; width: 100%;">Add <button class="preset-btn" onclick="addFromTLE()" style="margin-top: 10px; width: 100%;">Add
Satellite</button> Satellite</button>
</div> </div>
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -19,7 +19,7 @@
</div> </div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Frequency</span> <span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Frequency</span>
<span id="lpQuickFreq" style="font-size: 14px; font-family: 'JetBrains Mono', monospace; color: var(--text-primary);">---.--- MHz</span> <span id="lpQuickFreq" style="font-size: 14px; font-family: var(--font-mono); color: var(--text-primary);">---.--- MHz</span>
</div> </div>
<div style="display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; justify-content: space-between; align-items: center;">
<span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Signals</span> <span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Signals</span>
+318 -318
View File
@@ -1,318 +1,318 @@
<!-- Settings Modal --> <!-- Settings Modal -->
<div id="settingsModal" class="settings-modal" onclick="if(event.target === this) hideSettings()"> <div id="settingsModal" class="settings-modal" onclick="if(event.target === this) hideSettings()">
<div class="settings-content"> <div class="settings-content">
<div class="settings-header"> <div class="settings-header">
<h2> <h2>
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span> <span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
Settings Settings
</h2> </h2>
<button class="settings-close" onclick="hideSettings()">&times;</button> <button class="settings-close" onclick="hideSettings()">&times;</button>
</div> </div>
<div class="settings-tabs"> <div class="settings-tabs">
<button class="settings-tab active" data-tab="offline" onclick="switchSettingsTab('offline')">Offline</button> <button class="settings-tab active" data-tab="offline" onclick="switchSettingsTab('offline')">Offline</button>
<button class="settings-tab" data-tab="location" onclick="switchSettingsTab('location')">Location</button> <button class="settings-tab" data-tab="location" onclick="switchSettingsTab('location')">Location</button>
<button class="settings-tab" data-tab="display" onclick="switchSettingsTab('display')">Display</button> <button class="settings-tab" data-tab="display" onclick="switchSettingsTab('display')">Display</button>
<button class="settings-tab" data-tab="updates" onclick="switchSettingsTab('updates')">Updates</button> <button class="settings-tab" data-tab="updates" onclick="switchSettingsTab('updates')">Updates</button>
<button class="settings-tab" data-tab="tools" onclick="switchSettingsTab('tools')">Tools</button> <button class="settings-tab" data-tab="tools" onclick="switchSettingsTab('tools')">Tools</button>
<button class="settings-tab" data-tab="about" onclick="switchSettingsTab('about')">About</button> <button class="settings-tab" data-tab="about" onclick="switchSettingsTab('about')">About</button>
</div> </div>
<!-- Offline Section --> <!-- Offline Section -->
<div id="settings-offline" class="settings-section active"> <div id="settings-offline" class="settings-section active">
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Offline Mode</div> <div class="settings-group-title">Offline Mode</div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Enable Offline Mode</span> <span class="settings-label-text">Enable Offline Mode</span>
<span class="settings-label-desc">Use local assets instead of CDN</span> <span class="settings-label-desc">Use local assets instead of CDN</span>
</div> </div>
<label class="toggle-switch"> <label class="toggle-switch">
<input type="checkbox" id="offlineEnabled" onchange="Settings.toggleOfflineMode(this.checked)"> <input type="checkbox" id="offlineEnabled" onchange="Settings.toggleOfflineMode(this.checked)">
<span class="toggle-slider"></span> <span class="toggle-slider"></span>
</label> </label>
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Asset Sources</div> <div class="settings-group-title">Asset Sources</div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">JavaScript/CSS Libraries</span> <span class="settings-label-text">JavaScript/CSS Libraries</span>
<span class="settings-label-desc">Leaflet, Chart.js</span> <span class="settings-label-desc">Leaflet, Chart.js</span>
</div> </div>
<select id="assetsSource" class="settings-select" onchange="Settings.setAssetSource(this.value)"> <select id="assetsSource" class="settings-select" onchange="Settings.setAssetSource(this.value)">
<option value="cdn">CDN (Online)</option> <option value="cdn">CDN (Online)</option>
<option value="local">Local</option> <option value="local">Local</option>
</select> </select>
</div> </div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Web Fonts</span> <span class="settings-label-text">Web Fonts</span>
<span class="settings-label-desc">Inter, JetBrains Mono</span> <span class="settings-label-desc">Inter, Terminus</span>
</div> </div>
<select id="fontsSource" class="settings-select" onchange="Settings.setFontsSource(this.value)"> <select id="fontsSource" class="settings-select" onchange="Settings.setFontsSource(this.value)">
<option value="cdn">Google Fonts (Online)</option> <option value="cdn">Google Fonts (Online)</option>
<option value="local">Local</option> <option value="local">Local</option>
</select> </select>
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Map Tiles</div> <div class="settings-group-title">Map Tiles</div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Tile Provider</span> <span class="settings-label-text">Tile Provider</span>
<span class="settings-label-desc">Map background imagery</span> <span class="settings-label-desc">Map background imagery</span>
</div> </div>
<select id="tileProvider" class="settings-select" onchange="Settings.setTileProvider(this.value)"> <select id="tileProvider" class="settings-select" onchange="Settings.setTileProvider(this.value)">
<option value="openstreetmap">OpenStreetMap</option> <option value="openstreetmap">OpenStreetMap</option>
<option value="cartodb_dark">CartoDB Dark</option> <option value="cartodb_dark">CartoDB Dark</option>
<option value="cartodb_light">CartoDB Positron</option> <option value="cartodb_light">CartoDB Positron</option>
<option value="esri_world">ESRI World Imagery</option> <option value="esri_world">ESRI World Imagery</option>
<option value="custom">Custom URL</option> <option value="custom">Custom URL</option>
</select> </select>
</div> </div>
<div class="settings-row custom-url-row" id="customTileUrlRow" style="display: none;"> <div class="settings-row custom-url-row" id="customTileUrlRow" style="display: none;">
<div class="settings-label" style="width: 100%;"> <div class="settings-label" style="width: 100%;">
<span class="settings-label-text">Custom Tile URL</span> <span class="settings-label-text">Custom Tile URL</span>
<span class="settings-label-desc">e.g., http://localhost:8080/{z}/{x}/{y}.png</span> <span class="settings-label-desc">e.g., http://localhost:8080/{z}/{x}/{y}.png</span>
<input type="text" id="customTileUrl" class="settings-input" <input type="text" id="customTileUrl" class="settings-input"
placeholder="http://tile-server/{z}/{x}/{y}.png" placeholder="http://tile-server/{z}/{x}/{y}.png"
onchange="Settings.setCustomTileUrl(this.value)"> onchange="Settings.setCustomTileUrl(this.value)">
</div> </div>
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Local Asset Status</div> <div class="settings-group-title">Local Asset Status</div>
<div class="asset-status" id="assetStatus"> <div class="asset-status" id="assetStatus">
<div class="asset-status-row"> <div class="asset-status-row">
<span class="asset-name">Leaflet JS/CSS</span> <span class="asset-name">Leaflet JS/CSS</span>
<span class="asset-badge checking" id="statusLeaflet">Checking...</span> <span class="asset-badge checking" id="statusLeaflet">Checking...</span>
</div> </div>
<div class="asset-status-row"> <div class="asset-status-row">
<span class="asset-name">Chart.js</span> <span class="asset-name">Chart.js</span>
<span class="asset-badge checking" id="statusChartjs">Checking...</span> <span class="asset-badge checking" id="statusChartjs">Checking...</span>
</div> </div>
<div class="asset-status-row"> <div class="asset-status-row">
<span class="asset-name">Inter Font</span> <span class="asset-name">Inter Font</span>
<span class="asset-badge checking" id="statusInter">Checking...</span> <span class="asset-badge checking" id="statusInter">Checking...</span>
</div> </div>
<div class="asset-status-row"> <div class="asset-status-row">
<span class="asset-name">JetBrains Mono</span> <span class="asset-name">Terminus</span>
<span class="asset-badge checking" id="statusJetbrains">Checking...</span> <span class="asset-badge checking" id="statusJetbrains">Checking...</span>
</div> </div>
</div> </div>
<button class="check-assets-btn" onclick="Settings.checkAssets()"> <button class="check-assets-btn" onclick="Settings.checkAssets()">
Check Assets Check Assets
</button> </button>
</div> </div>
<div class="settings-info"> <div class="settings-info">
<strong>Note:</strong> Changes to asset sources require a page reload to take effect. <strong>Note:</strong> Changes to asset sources require a page reload to take effect.
Local assets must be available in <code>/static/vendor/</code>. Local assets must be available in <code>/static/vendor/</code>.
</div> </div>
</div> </div>
<!-- Location Section --> <!-- Location Section -->
<div id="settings-location" class="settings-section"> <div id="settings-location" class="settings-section">
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Observer Location</div> <div class="settings-group-title">Observer Location</div>
<p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;"> <p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;">
Set your geographic coordinates for satellite pass predictions and ISS tracking. Set your geographic coordinates for satellite pass predictions and ISS tracking.
</p> </p>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Latitude</span> <span class="settings-label-text">Latitude</span>
<span class="settings-label-desc">Decimal degrees (-90 to 90)</span> <span class="settings-label-desc">Decimal degrees (-90 to 90)</span>
</div> </div>
<input type="number" id="observerLatInput" class="settings-input" <input type="number" id="observerLatInput" class="settings-input"
step="0.0001" min="-90" max="90" placeholder="51.5074" step="0.0001" min="-90" max="90" placeholder="51.5074"
style="width: 120px; text-align: right;"> style="width: 120px; text-align: right;">
</div> </div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Longitude</span> <span class="settings-label-text">Longitude</span>
<span class="settings-label-desc">Decimal degrees (-180 to 180)</span> <span class="settings-label-desc">Decimal degrees (-180 to 180)</span>
</div> </div>
<input type="number" id="observerLonInput" class="settings-input" <input type="number" id="observerLonInput" class="settings-input"
step="0.0001" min="-180" max="180" placeholder="-0.1278" step="0.0001" min="-180" max="180" placeholder="-0.1278"
style="width: 120px; text-align: right;"> style="width: 120px; text-align: right;">
</div> </div>
<div style="display: flex; gap: 10px; margin-top: 15px;"> <div style="display: flex; gap: 10px; margin-top: 15px;">
<button class="check-assets-btn" onclick="detectLocationGPS(this)" style="flex: 1;"> <button class="check-assets-btn" onclick="detectLocationGPS(this)" style="flex: 1;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; vertical-align: -2px; margin-right: 5px;"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; vertical-align: -2px; margin-right: 5px;">
<circle cx="12" cy="12" r="10"/> <circle cx="12" cy="12" r="10"/>
<circle cx="12" cy="12" r="3"/> <circle cx="12" cy="12" r="3"/>
<line x1="12" y1="2" x2="12" y2="6"/> <line x1="12" y1="2" x2="12" y2="6"/>
<line x1="12" y1="18" x2="12" y2="22"/> <line x1="12" y1="18" x2="12" y2="22"/>
<line x1="2" y1="12" x2="6" y2="12"/> <line x1="2" y1="12" x2="6" y2="12"/>
<line x1="18" y1="12" x2="22" y2="12"/> <line x1="18" y1="12" x2="22" y2="12"/>
</svg> </svg>
Use GPS Use GPS
</button> </button>
<button class="check-assets-btn" onclick="saveObserverLocation()" style="flex: 1; background: var(--accent-cyan); color: #000;"> <button class="check-assets-btn" onclick="saveObserverLocation()" style="flex: 1; background: var(--accent-cyan); color: #000;">
Save Location Save Location
</button> </button>
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Current Location</div> <div class="settings-group-title">Current Location</div>
<div id="currentLocationDisplay" style="padding: 12px; background: var(--bg-tertiary); border-radius: 6px; font-family: 'JetBrains Mono', monospace; font-size: 12px;"> <div id="currentLocationDisplay" style="padding: 12px; background: var(--bg-tertiary); border-radius: 6px; font-family: var(--font-mono); font-size: 12px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 6px;"> <div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
<span style="color: var(--text-dim);">Latitude</span> <span style="color: var(--text-dim);">Latitude</span>
<span id="currentLatDisplay" style="color: var(--accent-cyan);">Not set</span> <span id="currentLatDisplay" style="color: var(--accent-cyan);">Not set</span>
</div> </div>
<div style="display: flex; justify-content: space-between;"> <div style="display: flex; justify-content: space-between;">
<span style="color: var(--text-dim);">Longitude</span> <span style="color: var(--text-dim);">Longitude</span>
<span id="currentLonDisplay" style="color: var(--accent-cyan);">Not set</span> <span id="currentLonDisplay" style="color: var(--accent-cyan);">Not set</span>
</div> </div>
</div> </div>
</div> </div>
<div class="settings-info"> <div class="settings-info">
<strong>Note:</strong> Location is used for ISS pass predictions in SSTV mode and satellite tracking. <strong>Note:</strong> Location is used for ISS pass predictions in SSTV mode and satellite tracking.
Your location is stored locally and never sent to external servers. Your location is stored locally and never sent to external servers.
</div> </div>
</div> </div>
<!-- Display Section --> <!-- Display Section -->
<div id="settings-display" class="settings-section"> <div id="settings-display" class="settings-section">
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Visual Preferences</div> <div class="settings-group-title">Visual Preferences</div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Theme</span> <span class="settings-label-text">Theme</span>
<span class="settings-label-desc">Color scheme preference</span> <span class="settings-label-desc">Color scheme preference</span>
</div> </div>
<select id="themeSelect" class="settings-select" onchange="setThemePreference(this.value)"> <select id="themeSelect" class="settings-select" onchange="setThemePreference(this.value)">
<option value="dark">Dark</option> <option value="dark">Dark</option>
<option value="light">Light</option> <option value="light">Light</option>
</select> </select>
</div> </div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Animations</span> <span class="settings-label-text">Animations</span>
<span class="settings-label-desc">Enable visual effects and animations</span> <span class="settings-label-desc">Enable visual effects and animations</span>
</div> </div>
<label class="toggle-switch"> <label class="toggle-switch">
<input type="checkbox" id="animationsEnabled" checked onchange="setAnimationsEnabled(this.checked)"> <input type="checkbox" id="animationsEnabled" checked onchange="setAnimationsEnabled(this.checked)">
<span class="toggle-slider"></span> <span class="toggle-slider"></span>
</label> </label>
</div> </div>
</div> </div>
</div> </div>
<!-- Updates Section --> <!-- Updates Section -->
<div id="settings-updates" class="settings-section"> <div id="settings-updates" class="settings-section">
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Update Status</div> <div class="settings-group-title">Update Status</div>
<div id="updateStatusContent" style="padding: 10px 0;"> <div id="updateStatusContent" style="padding: 10px 0;">
<div style="text-align: center; padding: 20px; color: var(--text-dim);"> <div style="text-align: center; padding: 20px; color: var(--text-dim);">
Loading update status... Loading update status...
</div> </div>
</div> </div>
<button class="check-assets-btn" onclick="checkForUpdatesManual()" style="margin-top: 10px;"> <button class="check-assets-btn" onclick="checkForUpdatesManual()" style="margin-top: 10px;">
Check Now Check Now
</button> </button>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Update Settings</div> <div class="settings-group-title">Update Settings</div>
<div class="settings-row"> <div class="settings-row">
<div class="settings-label"> <div class="settings-label">
<span class="settings-label-text">Auto-Check for Updates</span> <span class="settings-label-text">Auto-Check for Updates</span>
<span class="settings-label-desc">Periodically check GitHub for new releases</span> <span class="settings-label-desc">Periodically check GitHub for new releases</span>
</div> </div>
<label class="toggle-switch"> <label class="toggle-switch">
<input type="checkbox" id="updateCheckEnabled" checked onchange="toggleUpdateCheck(this.checked)"> <input type="checkbox" id="updateCheckEnabled" checked onchange="toggleUpdateCheck(this.checked)">
<span class="toggle-slider"></span> <span class="toggle-slider"></span>
</label> </label>
</div> </div>
</div> </div>
<div class="settings-info"> <div class="settings-info">
<strong>Note:</strong> Updates are fetched from GitHub and applied via git pull. <strong>Note:</strong> Updates are fetched from GitHub and applied via git pull.
Make sure you have git installed and the application is in a git repository. Make sure you have git installed and the application is in a git repository.
</div> </div>
</div> </div>
<!-- Tools Section --> <!-- Tools Section -->
<div id="settings-tools" class="settings-section"> <div id="settings-tools" class="settings-section">
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Tool Dependencies</div> <div class="settings-group-title">Tool Dependencies</div>
<p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;"> <p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;">
Check which external tools are installed for each mode. Check which external tools are installed for each mode.
<span style="color: var(--accent-green);"></span> = Installed, <span style="color: var(--accent-green);"></span> = Installed,
<span style="color: var(--accent-red);"></span> = Missing <span style="color: var(--accent-red);"></span> = Missing
</p> </p>
<div id="settingsToolsContent" style="max-height: 45vh; overflow-y: auto;"> <div id="settingsToolsContent" style="max-height: 45vh; overflow-y: auto;">
<div style="text-align: center; padding: 30px; color: var(--text-dim);"> <div style="text-align: center; padding: 30px; color: var(--text-dim);">
Loading dependencies... Loading dependencies...
</div> </div>
</div> </div>
</div> </div>
<div class="settings-group" style="margin-top: 15px;"> <div class="settings-group" style="margin-top: 15px;">
<div class="settings-group-title">Quick Install (Debian/Ubuntu)</div> <div class="settings-group-title">Quick Install (Debian/Ubuntu)</div>
<div style="background: var(--bg-tertiary); padding: 10px; border-radius: 4px; font-family: var(--font-mono); font-size: 10px; overflow-x: auto;"> <div style="background: var(--bg-tertiary); padding: 10px; border-radius: 4px; font-family: var(--font-mono); font-size: 10px; overflow-x: auto;">
<div>sudo apt install rtl-sdr multimon-ng rtl-433 aircrack-ng bluez dump1090-mutability hcxdumptool hcxtools</div> <div>sudo apt install rtl-sdr multimon-ng rtl-433 aircrack-ng bluez dump1090-mutability hcxdumptool hcxtools</div>
<div style="margin-top: 5px;">pip install skyfield flask</div> <div style="margin-top: 5px;">pip install skyfield flask</div>
</div> </div>
<div style="margin-top: 10px; font-size: 11px; color: var(--text-dim);"> <div style="margin-top: 10px; font-size: 11px; color: var(--text-dim);">
<strong>Note:</strong> ACARS decoding requires <code>acarsdec</code> which must be built from source. <strong>Note:</strong> ACARS decoding requires <code>acarsdec</code> which must be built from source.
See <a href="https://github.com/TLeconte/acarsdec" target="_blank" style="color: var(--accent-cyan);">github.com/TLeconte/acarsdec</a> or run <code>./setup.sh</code> for automated installation. See <a href="https://github.com/TLeconte/acarsdec" target="_blank" style="color: var(--accent-cyan);">github.com/TLeconte/acarsdec</a> or run <code>./setup.sh</code> for automated installation.
</div> </div>
</div> </div>
</div> </div>
<!-- About Section --> <!-- About Section -->
<div id="settings-about" class="settings-section"> <div id="settings-about" class="settings-section">
<div class="settings-group"> <div class="settings-group">
<div class="about-info"> <div class="about-info">
<p><strong>iNTERCEPT</strong> - Signal Intelligence Platform</p> <p><strong>iNTERCEPT</strong> - Signal Intelligence Platform</p>
<p>Version: <span class="about-version">{{ version }}</span></p> <p>Version: <span class="about-version">{{ version }}</span></p>
<p> <p>
A unified web interface for software-defined radio (SDR) tools, A unified web interface for software-defined radio (SDR) tools,
supporting pager decoding, sensor monitoring, aircraft tracking, supporting pager decoding, sensor monitoring, aircraft tracking,
WiFi/Bluetooth scanning, and more. WiFi/Bluetooth scanning, and more.
</p> </p>
<p> <p>
<a href="https://github.com/smittix/intercept" target="_blank">GitHub Repository</a> <a href="https://github.com/smittix/intercept" target="_blank">GitHub Repository</a>
</p> </p>
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<div class="settings-group-title">Support the Project</div> <div class="settings-group-title">Support the Project</div>
<p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;"> <p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;">
If you find iNTERCEPT useful, consider supporting its development. If you find iNTERCEPT useful, consider supporting its development.
</p> </p>
<a href="https://buymeacoffee.com/smittix" target="_blank" rel="noopener noreferrer" class="donate-btn"> <a href="https://buymeacoffee.com/smittix" target="_blank" rel="noopener noreferrer" class="donate-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 18px; height: 18px; vertical-align: -3px; margin-right: 8px;"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 18px; height: 18px; vertical-align: -3px; margin-right: 8px;">
<path d="M17 8h1a4 4 0 1 1 0 8h-1"/> <path d="M17 8h1a4 4 0 1 1 0 8h-1"/>
<path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"/> <path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"/>
<line x1="6" y1="2" x2="6" y2="4"/> <line x1="6" y1="2" x2="6" y2="4"/>
<line x1="10" y1="2" x2="10" y2="4"/> <line x1="10" y1="2" x2="10" y2="4"/>
<line x1="14" y1="2" x2="14" y2="4"/> <line x1="14" y1="2" x2="14" y2="4"/>
</svg> </svg>
Buy Me a Coffee Buy Me a Coffee
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
File diff suppressed because it is too large Load Diff