mirror of
https://github.com/smittix/intercept.git
synced 2026-06-08 14:11:54 -07:00
fix: sweep final hardcoded cyan from mode JS files and CSS
- proximity-radar.js: fix missed dot stroke in new-device creation path - gps.js: GPS constellation color via object getter; globe atmosphere reads CSS var - websdr.js: globe atmosphere, map markers, popup buttons, point label use CSS var - subghz.js: canvas strokeStyle reads --accent-cyan - sstv.js: ISS track polyline reads --accent-cyan - app.js: info message border-left uses var(--accent-cyan) - subghz.css, gps.css: replace all #00d4ff with var(--accent-cyan) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -364,7 +364,7 @@
|
||||
}
|
||||
|
||||
/* Constellation colors */
|
||||
.gps-const-gps { background-color: #00d4ff; }
|
||||
.gps-const-gps { background-color: var(--accent-cyan); }
|
||||
.gps-const-glonass { background-color: #00ff88; }
|
||||
.gps-const-galileo { background-color: #ff8800; }
|
||||
.gps-const-beidou { background-color: #ff4466; }
|
||||
|
||||
+41
-41
@@ -93,9 +93,9 @@
|
||||
}
|
||||
|
||||
.subghz-preset-btn:hover {
|
||||
background: var(--accent-cyan, #00d4ff);
|
||||
background: var(--accent-cyan, var(--accent-cyan));
|
||||
color: var(--text-inverse);
|
||||
border-color: var(--accent-cyan, #00d4ff);
|
||||
border-color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
/* Tab navigation for RX / Decode / Sweep */
|
||||
@@ -126,8 +126,8 @@
|
||||
}
|
||||
|
||||
.subghz-tab.active {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
border-bottom-color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
border-bottom-color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-tab-content {
|
||||
@@ -225,7 +225,7 @@
|
||||
}
|
||||
|
||||
.subghz-status-dot.decode {
|
||||
background: #00d4ff;
|
||||
background: var(--accent-cyan);
|
||||
animation: subghz-pulse 0.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
}
|
||||
|
||||
.subghz-status-timer {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
/* Control buttons */
|
||||
@@ -296,13 +296,13 @@
|
||||
|
||||
.subghz-btn:hover {
|
||||
background: var(--bg-tertiary, #1a1f2e);
|
||||
border-color: var(--accent-cyan, #00d4ff);
|
||||
border-color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-btn.active {
|
||||
background: rgba(0, 212, 255, 0.1);
|
||||
border-color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
border-color: var(--accent-cyan, var(--accent-cyan));
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-btn.start {
|
||||
@@ -456,7 +456,7 @@
|
||||
|
||||
.subghz-capture-tag.auto {
|
||||
border-color: rgba(0, 212, 255, 0.55);
|
||||
color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
background: rgba(0, 212, 255, 0.12);
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@
|
||||
}
|
||||
|
||||
.subghz-capture-freq {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -526,8 +526,8 @@
|
||||
}
|
||||
|
||||
.subghz-capture-actions button.trim-btn:hover {
|
||||
color: #00d4ff;
|
||||
border-color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
border-color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
.subghz-capture-actions button.delete-btn:hover {
|
||||
@@ -537,7 +537,7 @@
|
||||
|
||||
.subghz-capture-actions button.select-btn {
|
||||
border-color: rgba(0, 212, 255, 0.5);
|
||||
color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
.subghz-capture-actions button.select-btn.selected {
|
||||
@@ -622,7 +622,7 @@
|
||||
}
|
||||
|
||||
.subghz-decode-model {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -693,7 +693,7 @@
|
||||
}
|
||||
|
||||
.subghz-tx-modal .tx-freq {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
font-weight: 600;
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
}
|
||||
@@ -754,7 +754,7 @@
|
||||
margin-top: 8px !important;
|
||||
margin-bottom: 0 !important;
|
||||
font-size: 11px !important;
|
||||
color: var(--accent-cyan, #00d4ff) !important;
|
||||
color: var(--accent-cyan, var(--accent-cyan)) !important;
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
}
|
||||
|
||||
@@ -807,7 +807,7 @@
|
||||
margin: 0 0 8px 0;
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
font-size: 10px;
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-tx-burst-marker {
|
||||
@@ -864,7 +864,7 @@
|
||||
border: 1px solid rgba(0, 212, 255, 0.5);
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
@@ -902,7 +902,7 @@
|
||||
|
||||
.subghz-tx-trim-btn {
|
||||
background: rgba(0, 212, 255, 0.14);
|
||||
color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
border-color: rgba(0, 212, 255, 0.55) !important;
|
||||
}
|
||||
|
||||
@@ -952,7 +952,7 @@
|
||||
}
|
||||
|
||||
.subghz-sweep-tooltip .tip-freq {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-sweep-tooltip .tip-power {
|
||||
@@ -1044,8 +1044,8 @@
|
||||
|
||||
.subghz-action-btn.decode:hover {
|
||||
background: rgba(0, 212, 255, 0.12);
|
||||
border-color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
border-color: var(--accent-cyan, var(--accent-cyan));
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-action-btn.capture:hover {
|
||||
@@ -1092,7 +1092,7 @@
|
||||
}
|
||||
|
||||
.subghz-peak-item .peak-freq {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-peak-item .peak-power {
|
||||
@@ -1148,7 +1148,7 @@
|
||||
}
|
||||
|
||||
.subghz-strip-dot.rx { background: var(--neon-green); }
|
||||
.subghz-strip-dot.decode { background: #00d4ff; }
|
||||
.subghz-strip-dot.decode { background: var(--accent-cyan); }
|
||||
.subghz-strip-dot.tx { background: #ff4444; }
|
||||
.subghz-strip-dot.sweep { background: var(--neon-orange); }
|
||||
|
||||
@@ -1169,7 +1169,7 @@
|
||||
color: var(--text-primary, #e0e0e0);
|
||||
}
|
||||
|
||||
.subghz-strip-value.accent-cyan { color: var(--accent-cyan, #00d4ff); }
|
||||
.subghz-strip-value.accent-cyan { color: var(--accent-cyan, var(--accent-cyan)); }
|
||||
.subghz-strip-value.accent-green { color: var(--neon-green); }
|
||||
.subghz-strip-value.accent-orange { color: var(--neon-orange); }
|
||||
|
||||
@@ -1181,7 +1181,7 @@
|
||||
}
|
||||
|
||||
.subghz-strip-timer {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
font-weight: 600;
|
||||
min-width: 40px;
|
||||
}
|
||||
@@ -1274,7 +1274,7 @@
|
||||
}
|
||||
|
||||
.subghz-phase-step.active {
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
text-shadow: 0 0 6px rgba(0, 212, 255, 0.3);
|
||||
}
|
||||
|
||||
@@ -1329,12 +1329,12 @@
|
||||
|
||||
.subghz-burst-indicator.recent {
|
||||
border-color: rgba(0, 212, 255, 0.45);
|
||||
color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
background: rgba(0, 212, 255, 0.1);
|
||||
}
|
||||
|
||||
.subghz-burst-indicator.recent .subghz-burst-dot {
|
||||
background: #00d4ff;
|
||||
background: var(--accent-cyan);
|
||||
}
|
||||
|
||||
.subghz-console-toggle {
|
||||
@@ -1382,7 +1382,7 @@
|
||||
}
|
||||
|
||||
.subghz-log-msg { color: var(--text-secondary, #999); }
|
||||
.subghz-log-msg.info { color: var(--accent-cyan, #00d4ff); }
|
||||
.subghz-log-msg.info { color: var(--accent-cyan, var(--accent-cyan)); }
|
||||
.subghz-log-msg.success { color: var(--neon-green); }
|
||||
.subghz-log-msg.warn { color: var(--neon-orange); }
|
||||
.subghz-log-msg.error { color: var(--accent-red, #ff4444); }
|
||||
@@ -1405,7 +1405,7 @@
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
@@ -1446,8 +1446,8 @@
|
||||
}
|
||||
|
||||
.subghz-hub-card--cyan { border-color: rgba(0, 212, 255, 0.2); }
|
||||
.subghz-hub-card--cyan:hover { border-color: var(--accent-cyan, #00d4ff); background: rgba(0, 212, 255, 0.05); }
|
||||
.subghz-hub-card--cyan .subghz-hub-icon { color: var(--accent-cyan, #00d4ff); }
|
||||
.subghz-hub-card--cyan:hover { border-color: var(--accent-cyan, var(--accent-cyan)); background: rgba(0, 212, 255, 0.05); }
|
||||
.subghz-hub-card--cyan .subghz-hub-icon { color: var(--accent-cyan, var(--accent-cyan)); }
|
||||
|
||||
.subghz-hub-card--green { border-color: rgba(0, 255, 136, 0.2); }
|
||||
.subghz-hub-card--green:hover { border-color: var(--neon-green); background: rgba(0, 255, 136, 0.05); }
|
||||
@@ -1528,7 +1528,7 @@
|
||||
.subghz-saved-selection-count {
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
font-size: 10px;
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
@@ -1545,8 +1545,8 @@
|
||||
}
|
||||
|
||||
.subghz-op-back-btn:hover {
|
||||
border-color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
border-color: var(--accent-cyan, var(--accent-cyan));
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-op-panel-title {
|
||||
@@ -1667,7 +1667,7 @@
|
||||
color: var(--text-primary, #e0e0e0);
|
||||
}
|
||||
|
||||
.subghz-rx-info-value.accent-cyan { color: var(--accent-cyan, #00d4ff); }
|
||||
.subghz-rx-info-value.accent-cyan { color: var(--accent-cyan, var(--accent-cyan)); }
|
||||
|
||||
.subghz-rx-level-wrapper {
|
||||
display: flex;
|
||||
@@ -1735,7 +1735,7 @@
|
||||
}
|
||||
|
||||
.subghz-rx-burst-pill.recent {
|
||||
color: #00d4ff;
|
||||
color: var(--accent-cyan);
|
||||
border-color: rgba(0, 212, 255, 0.65);
|
||||
background: rgba(0, 212, 255, 0.12);
|
||||
}
|
||||
@@ -1861,8 +1861,8 @@
|
||||
}
|
||||
|
||||
.subghz-wf-pause-btn:hover {
|
||||
border-color: var(--accent-cyan, #00d4ff);
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
border-color: var(--accent-cyan, var(--accent-cyan));
|
||||
color: var(--accent-cyan, var(--accent-cyan));
|
||||
}
|
||||
|
||||
.subghz-wf-pause-btn.paused {
|
||||
|
||||
@@ -308,7 +308,7 @@ const ProximityRadar = (function() {
|
||||
dot.setAttribute('r', dotSize);
|
||||
dot.setAttribute('fill', color);
|
||||
dot.setAttribute('fill-opacity', isSelected ? 1 : 0.4 + confidence * 0.5);
|
||||
dot.setAttribute('stroke', isSelected ? '#00d4ff' : color);
|
||||
dot.setAttribute('stroke', isSelected ? _accent() : color);
|
||||
dot.setAttribute('stroke-width', isSelected ? 2 : 1);
|
||||
innerG.appendChild(dot);
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ function showInfo(text) {
|
||||
|
||||
const infoEl = document.createElement('div');
|
||||
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: "Roboto Condensed", "Arial Narrow", sans-serif; 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 var(--accent-cyan); font-family: "Roboto Condensed", "Arial Narrow", sans-serif; font-size: 11px; color: #888; word-break: break-all;';
|
||||
infoEl.textContent = text;
|
||||
output.insertBefore(infoEl, output.firstChild);
|
||||
}
|
||||
|
||||
+241
-241
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* GPS Mode
|
||||
* Live GPS data display with satellite sky view, signal strength bars,
|
||||
* position/velocity/DOP readout. Connects to gpsd via backend SSE stream.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GPS Mode
|
||||
* Live GPS data display with satellite sky view, signal strength bars,
|
||||
* position/velocity/DOP readout. Connects to gpsd via backend SSE stream.
|
||||
*/
|
||||
|
||||
const GPS = (function() {
|
||||
let connected = false;
|
||||
let lastPosition = null;
|
||||
@@ -17,10 +17,10 @@ const GPS = (function() {
|
||||
|
||||
// Constellation color map
|
||||
const CONST_COLORS = {
|
||||
'GPS': '#00d4ff',
|
||||
get GPS() { return getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff'; },
|
||||
'GLONASS': '#00ff88',
|
||||
'Galileo': '#ff8800',
|
||||
'BeiDou': '#ff4466',
|
||||
'Galileo': '#ff8800',
|
||||
'BeiDou': '#ff4466',
|
||||
'SBAS': '#ffdd00',
|
||||
'QZSS': '#cc66ff',
|
||||
};
|
||||
@@ -266,7 +266,7 @@ const GPS = (function() {
|
||||
.backgroundColor('rgba(0,0,0,0)')
|
||||
.globeImageUrl(GPS_GLOBE_TEXTURE_URL)
|
||||
.showAtmosphere(true)
|
||||
.atmosphereColor('#3bb9ff')
|
||||
.atmosphereColor(getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#3bb9ff')
|
||||
.atmosphereAltitude(0.17)
|
||||
.pointRadius('radius')
|
||||
.pointAltitude('altitude')
|
||||
@@ -462,16 +462,16 @@ const GPS = (function() {
|
||||
fetch('/gps/auto-connect', { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'connected') {
|
||||
connected = true;
|
||||
updateConnectionUI(true, data.has_fix);
|
||||
if (data.position) {
|
||||
lastPosition = data.position;
|
||||
updatePositionUI(data.position);
|
||||
}
|
||||
if (data.sky) {
|
||||
lastSky = data.sky;
|
||||
updateSkyUI(data.sky);
|
||||
if (data.status === 'connected') {
|
||||
connected = true;
|
||||
updateConnectionUI(true, data.has_fix);
|
||||
if (data.position) {
|
||||
lastPosition = data.position;
|
||||
updatePositionUI(data.position);
|
||||
}
|
||||
if (data.sky) {
|
||||
lastSky = data.sky;
|
||||
updateSkyUI(data.sky);
|
||||
}
|
||||
subscribeToStream();
|
||||
startSkyPolling();
|
||||
@@ -484,14 +484,14 @@ const GPS = (function() {
|
||||
} else {
|
||||
connected = false;
|
||||
updateConnectionUI(false, false, 'error', data.message || 'gpsd not available');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
connected = false;
|
||||
updateConnectionUI(false, false, 'error', 'Connection failed — is the server running?');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
connected = false;
|
||||
updateConnectionUI(false, false, 'error', 'Connection failed — is the server running?');
|
||||
});
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
unsubscribeFromStream();
|
||||
stopSkyPolling();
|
||||
@@ -501,10 +501,10 @@ const GPS = (function() {
|
||||
connected = false;
|
||||
updateConnectionUI(false);
|
||||
});
|
||||
}
|
||||
|
||||
function onGpsStreamData(data) {
|
||||
if (!connected) return;
|
||||
}
|
||||
|
||||
function onGpsStreamData(data) {
|
||||
if (!connected) return;
|
||||
if (data.type === 'position') {
|
||||
lastPosition = data;
|
||||
updatePositionUI(data);
|
||||
@@ -517,7 +517,7 @@ const GPS = (function() {
|
||||
updateSkyUI(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startSkyPolling() {
|
||||
stopSkyPolling();
|
||||
// Poll satellite data every 5 seconds as a reliable fallback
|
||||
@@ -526,22 +526,22 @@ const GPS = (function() {
|
||||
skyPollTimer = setInterval(pollSatellites, 5000);
|
||||
}
|
||||
|
||||
function stopSkyPolling() {
|
||||
if (skyPollTimer) {
|
||||
clearInterval(skyPollTimer);
|
||||
skyPollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function stopSkyPolling() {
|
||||
if (skyPollTimer) {
|
||||
clearInterval(skyPollTimer);
|
||||
skyPollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function pollSatellites() {
|
||||
if (!connected) return;
|
||||
fetch('/gps/satellites')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'ok' && data.sky) {
|
||||
lastSky = data.sky;
|
||||
updateSkyUI(data.sky);
|
||||
}
|
||||
if (data.status === 'ok' && data.sky) {
|
||||
lastSky = data.sky;
|
||||
updateSkyUI(data.sky);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -589,141 +589,141 @@ const GPS = (function() {
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function subscribeToStream() {
|
||||
// Subscribe to the global GPS stream instead of opening a separate SSE connection
|
||||
if (typeof addGpsStreamSubscriber === 'function') {
|
||||
addGpsStreamSubscriber(onGpsStreamData);
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribeFromStream() {
|
||||
if (typeof removeGpsStreamSubscriber === 'function') {
|
||||
removeGpsStreamSubscriber(onGpsStreamData);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================
|
||||
// UI Updates
|
||||
// ========================
|
||||
|
||||
function updateConnectionUI(isConnected, hasFix, state, message) {
|
||||
const dot = document.getElementById('gpsStatusDot');
|
||||
const text = document.getElementById('gpsStatusText');
|
||||
const connectBtn = document.getElementById('gpsConnectBtn');
|
||||
const disconnectBtn = document.getElementById('gpsDisconnectBtn');
|
||||
const devicePath = document.getElementById('gpsDevicePath');
|
||||
|
||||
if (dot) {
|
||||
dot.className = 'gps-status-dot';
|
||||
if (state === 'connecting') dot.classList.add('waiting');
|
||||
else if (state === 'error') dot.classList.add('error');
|
||||
else if (isConnected && hasFix) dot.classList.add('connected');
|
||||
else if (isConnected) dot.classList.add('waiting');
|
||||
}
|
||||
if (text) {
|
||||
if (state === 'connecting') text.textContent = 'Connecting...';
|
||||
else if (state === 'error') text.textContent = message || 'Connection failed';
|
||||
else if (isConnected && hasFix) text.textContent = 'Connected (Fix)';
|
||||
else if (isConnected) text.textContent = 'Connected (No Fix)';
|
||||
else text.textContent = 'Disconnected';
|
||||
}
|
||||
if (connectBtn) {
|
||||
connectBtn.style.display = isConnected ? 'none' : '';
|
||||
connectBtn.disabled = state === 'connecting';
|
||||
}
|
||||
if (disconnectBtn) disconnectBtn.style.display = isConnected ? '' : 'none';
|
||||
if (devicePath) devicePath.textContent = isConnected ? 'gpsd://localhost:2947' : '';
|
||||
}
|
||||
|
||||
function updatePositionUI(pos) {
|
||||
// Sidebar fields
|
||||
setText('gpsLat', pos.latitude != null ? pos.latitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsLon', pos.longitude != null ? pos.longitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsAlt', pos.altitude != null ? pos.altitude.toFixed(1) + ' m' : '---');
|
||||
setText('gpsSpeed', pos.speed != null ? (pos.speed * 3.6).toFixed(1) + ' km/h' : '---');
|
||||
setText('gpsHeading', pos.heading != null ? pos.heading.toFixed(1) + '\u00b0' : '---');
|
||||
setText('gpsClimb', pos.climb != null ? pos.climb.toFixed(2) + ' m/s' : '---');
|
||||
|
||||
// Fix type
|
||||
const fixEl = document.getElementById('gpsFixType');
|
||||
if (fixEl) {
|
||||
const fq = pos.fix_quality;
|
||||
if (fq === 3) fixEl.innerHTML = '<span class="gps-fix-badge fix-3d">3D FIX</span>';
|
||||
else if (fq === 2) fixEl.innerHTML = '<span class="gps-fix-badge fix-2d">2D FIX</span>';
|
||||
else fixEl.innerHTML = '<span class="gps-fix-badge no-fix">NO FIX</span>';
|
||||
}
|
||||
|
||||
// Error estimates
|
||||
const eph = (pos.epx != null && pos.epy != null) ? Math.sqrt(pos.epx * pos.epx + pos.epy * pos.epy) : null;
|
||||
setText('gpsEph', eph != null ? eph.toFixed(1) + ' m' : '---');
|
||||
setText('gpsEpv', pos.epv != null ? pos.epv.toFixed(1) + ' m' : '---');
|
||||
setText('gpsEps', pos.eps != null ? pos.eps.toFixed(2) + ' m/s' : '---');
|
||||
|
||||
// GPS time
|
||||
if (pos.timestamp) {
|
||||
const t = new Date(pos.timestamp);
|
||||
setText('gpsTime', t.toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC'));
|
||||
}
|
||||
|
||||
// Visuals: position panel
|
||||
setText('gpsVisPosLat', pos.latitude != null ? pos.latitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsVisPosLon', pos.longitude != null ? pos.longitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsVisPosAlt', pos.altitude != null ? pos.altitude.toFixed(1) + ' m' : '---');
|
||||
setText('gpsVisPosSpeed', pos.speed != null ? (pos.speed * 3.6).toFixed(1) + ' km/h' : '---');
|
||||
setText('gpsVisPosHeading', pos.heading != null ? pos.heading.toFixed(1) + '\u00b0' : '---');
|
||||
setText('gpsVisPosClimb', pos.climb != null ? pos.climb.toFixed(2) + ' m/s' : '---');
|
||||
|
||||
// Visuals: fix badge
|
||||
const visFixEl = document.getElementById('gpsVisFixBadge');
|
||||
if (visFixEl) {
|
||||
const fq = pos.fix_quality;
|
||||
if (fq === 3) { visFixEl.textContent = '3D FIX'; visFixEl.className = 'gps-fix-badge fix-3d'; }
|
||||
else if (fq === 2) { visFixEl.textContent = '2D FIX'; visFixEl.className = 'gps-fix-badge fix-2d'; }
|
||||
else { visFixEl.textContent = 'NO FIX'; visFixEl.className = 'gps-fix-badge no-fix'; }
|
||||
}
|
||||
|
||||
// Visuals: GPS time
|
||||
if (pos.timestamp) {
|
||||
const t = new Date(pos.timestamp);
|
||||
setText('gpsVisTime', t.toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC'));
|
||||
}
|
||||
}
|
||||
|
||||
function updateSkyUI(sky) {
|
||||
// Sidebar sat counts
|
||||
setText('gpsSatUsed', sky.usat != null ? sky.usat : '-');
|
||||
setText('gpsSatTotal', sky.nsat != null ? sky.nsat : '-');
|
||||
|
||||
// DOP values
|
||||
setDop('gpsHdop', sky.hdop);
|
||||
setDop('gpsVdop', sky.vdop);
|
||||
setDop('gpsPdop', sky.pdop);
|
||||
setDop('gpsTdop', sky.tdop);
|
||||
setDop('gpsGdop', sky.gdop);
|
||||
|
||||
// Visuals
|
||||
drawSkyView(sky.satellites || []);
|
||||
drawSignalBars(sky.satellites || []);
|
||||
}
|
||||
|
||||
function setDop(id, val) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
if (val == null) { el.textContent = '---'; el.className = 'gps-info-value gps-mono'; return; }
|
||||
el.textContent = val.toFixed(1);
|
||||
let cls = 'gps-info-value gps-mono ';
|
||||
if (val <= 2) cls += 'gps-dop-good';
|
||||
else if (val <= 5) cls += 'gps-dop-moderate';
|
||||
else cls += 'gps-dop-poor';
|
||||
el.className = cls;
|
||||
}
|
||||
|
||||
function setText(id, val) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.textContent = val;
|
||||
}
|
||||
|
||||
|
||||
function subscribeToStream() {
|
||||
// Subscribe to the global GPS stream instead of opening a separate SSE connection
|
||||
if (typeof addGpsStreamSubscriber === 'function') {
|
||||
addGpsStreamSubscriber(onGpsStreamData);
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribeFromStream() {
|
||||
if (typeof removeGpsStreamSubscriber === 'function') {
|
||||
removeGpsStreamSubscriber(onGpsStreamData);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================
|
||||
// UI Updates
|
||||
// ========================
|
||||
|
||||
function updateConnectionUI(isConnected, hasFix, state, message) {
|
||||
const dot = document.getElementById('gpsStatusDot');
|
||||
const text = document.getElementById('gpsStatusText');
|
||||
const connectBtn = document.getElementById('gpsConnectBtn');
|
||||
const disconnectBtn = document.getElementById('gpsDisconnectBtn');
|
||||
const devicePath = document.getElementById('gpsDevicePath');
|
||||
|
||||
if (dot) {
|
||||
dot.className = 'gps-status-dot';
|
||||
if (state === 'connecting') dot.classList.add('waiting');
|
||||
else if (state === 'error') dot.classList.add('error');
|
||||
else if (isConnected && hasFix) dot.classList.add('connected');
|
||||
else if (isConnected) dot.classList.add('waiting');
|
||||
}
|
||||
if (text) {
|
||||
if (state === 'connecting') text.textContent = 'Connecting...';
|
||||
else if (state === 'error') text.textContent = message || 'Connection failed';
|
||||
else if (isConnected && hasFix) text.textContent = 'Connected (Fix)';
|
||||
else if (isConnected) text.textContent = 'Connected (No Fix)';
|
||||
else text.textContent = 'Disconnected';
|
||||
}
|
||||
if (connectBtn) {
|
||||
connectBtn.style.display = isConnected ? 'none' : '';
|
||||
connectBtn.disabled = state === 'connecting';
|
||||
}
|
||||
if (disconnectBtn) disconnectBtn.style.display = isConnected ? '' : 'none';
|
||||
if (devicePath) devicePath.textContent = isConnected ? 'gpsd://localhost:2947' : '';
|
||||
}
|
||||
|
||||
function updatePositionUI(pos) {
|
||||
// Sidebar fields
|
||||
setText('gpsLat', pos.latitude != null ? pos.latitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsLon', pos.longitude != null ? pos.longitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsAlt', pos.altitude != null ? pos.altitude.toFixed(1) + ' m' : '---');
|
||||
setText('gpsSpeed', pos.speed != null ? (pos.speed * 3.6).toFixed(1) + ' km/h' : '---');
|
||||
setText('gpsHeading', pos.heading != null ? pos.heading.toFixed(1) + '\u00b0' : '---');
|
||||
setText('gpsClimb', pos.climb != null ? pos.climb.toFixed(2) + ' m/s' : '---');
|
||||
|
||||
// Fix type
|
||||
const fixEl = document.getElementById('gpsFixType');
|
||||
if (fixEl) {
|
||||
const fq = pos.fix_quality;
|
||||
if (fq === 3) fixEl.innerHTML = '<span class="gps-fix-badge fix-3d">3D FIX</span>';
|
||||
else if (fq === 2) fixEl.innerHTML = '<span class="gps-fix-badge fix-2d">2D FIX</span>';
|
||||
else fixEl.innerHTML = '<span class="gps-fix-badge no-fix">NO FIX</span>';
|
||||
}
|
||||
|
||||
// Error estimates
|
||||
const eph = (pos.epx != null && pos.epy != null) ? Math.sqrt(pos.epx * pos.epx + pos.epy * pos.epy) : null;
|
||||
setText('gpsEph', eph != null ? eph.toFixed(1) + ' m' : '---');
|
||||
setText('gpsEpv', pos.epv != null ? pos.epv.toFixed(1) + ' m' : '---');
|
||||
setText('gpsEps', pos.eps != null ? pos.eps.toFixed(2) + ' m/s' : '---');
|
||||
|
||||
// GPS time
|
||||
if (pos.timestamp) {
|
||||
const t = new Date(pos.timestamp);
|
||||
setText('gpsTime', t.toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC'));
|
||||
}
|
||||
|
||||
// Visuals: position panel
|
||||
setText('gpsVisPosLat', pos.latitude != null ? pos.latitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsVisPosLon', pos.longitude != null ? pos.longitude.toFixed(6) + '\u00b0' : '---');
|
||||
setText('gpsVisPosAlt', pos.altitude != null ? pos.altitude.toFixed(1) + ' m' : '---');
|
||||
setText('gpsVisPosSpeed', pos.speed != null ? (pos.speed * 3.6).toFixed(1) + ' km/h' : '---');
|
||||
setText('gpsVisPosHeading', pos.heading != null ? pos.heading.toFixed(1) + '\u00b0' : '---');
|
||||
setText('gpsVisPosClimb', pos.climb != null ? pos.climb.toFixed(2) + ' m/s' : '---');
|
||||
|
||||
// Visuals: fix badge
|
||||
const visFixEl = document.getElementById('gpsVisFixBadge');
|
||||
if (visFixEl) {
|
||||
const fq = pos.fix_quality;
|
||||
if (fq === 3) { visFixEl.textContent = '3D FIX'; visFixEl.className = 'gps-fix-badge fix-3d'; }
|
||||
else if (fq === 2) { visFixEl.textContent = '2D FIX'; visFixEl.className = 'gps-fix-badge fix-2d'; }
|
||||
else { visFixEl.textContent = 'NO FIX'; visFixEl.className = 'gps-fix-badge no-fix'; }
|
||||
}
|
||||
|
||||
// Visuals: GPS time
|
||||
if (pos.timestamp) {
|
||||
const t = new Date(pos.timestamp);
|
||||
setText('gpsVisTime', t.toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC'));
|
||||
}
|
||||
}
|
||||
|
||||
function updateSkyUI(sky) {
|
||||
// Sidebar sat counts
|
||||
setText('gpsSatUsed', sky.usat != null ? sky.usat : '-');
|
||||
setText('gpsSatTotal', sky.nsat != null ? sky.nsat : '-');
|
||||
|
||||
// DOP values
|
||||
setDop('gpsHdop', sky.hdop);
|
||||
setDop('gpsVdop', sky.vdop);
|
||||
setDop('gpsPdop', sky.pdop);
|
||||
setDop('gpsTdop', sky.tdop);
|
||||
setDop('gpsGdop', sky.gdop);
|
||||
|
||||
// Visuals
|
||||
drawSkyView(sky.satellites || []);
|
||||
drawSignalBars(sky.satellites || []);
|
||||
}
|
||||
|
||||
function setDop(id, val) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
if (val == null) { el.textContent = '---'; el.className = 'gps-info-value gps-mono'; return; }
|
||||
el.textContent = val.toFixed(1);
|
||||
let cls = 'gps-info-value gps-mono ';
|
||||
if (val <= 2) cls += 'gps-dop-good';
|
||||
else if (val <= 5) cls += 'gps-dop-moderate';
|
||||
else cls += 'gps-dop-poor';
|
||||
el.className = cls;
|
||||
}
|
||||
|
||||
function setText(id, val) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.textContent = val;
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Sky View Globe (WebGL with 2D fallback)
|
||||
// ========================
|
||||
@@ -1478,60 +1478,60 @@ const GPS = (function() {
|
||||
(num & 255) / 255,
|
||||
];
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Signal Strength Bars
|
||||
// ========================
|
||||
|
||||
function drawSignalBars(satellites) {
|
||||
const container = document.getElementById('gpsSignalBars');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
if (satellites.length === 0) return;
|
||||
|
||||
// Sort: used first, then by PRN
|
||||
const sorted = [...satellites].sort((a, b) => {
|
||||
if (a.used !== b.used) return a.used ? -1 : 1;
|
||||
return a.prn - b.prn;
|
||||
});
|
||||
|
||||
const maxSnr = 50; // dB-Hz typical max for display
|
||||
|
||||
sorted.forEach(sat => {
|
||||
const snr = sat.snr || 0;
|
||||
const heightPct = Math.min(snr / maxSnr * 100, 100);
|
||||
const color = CONST_COLORS[sat.constellation] || CONST_COLORS['GPS'];
|
||||
const constClass = 'gps-const-' + (sat.constellation || 'GPS').toLowerCase();
|
||||
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'gps-signal-bar-wrap';
|
||||
|
||||
const snrLabel = document.createElement('span');
|
||||
snrLabel.className = 'gps-signal-snr';
|
||||
snrLabel.textContent = snr > 0 ? Math.round(snr) : '';
|
||||
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'gps-signal-bar ' + constClass + (sat.used ? '' : ' unused');
|
||||
bar.style.height = Math.max(heightPct, 2) + '%';
|
||||
bar.title = `PRN ${sat.prn} (${sat.constellation}) - ${Math.round(snr)} dB-Hz${sat.used ? ' [USED]' : ''}`;
|
||||
|
||||
const prn = document.createElement('span');
|
||||
prn.className = 'gps-signal-prn';
|
||||
prn.textContent = sat.prn;
|
||||
|
||||
wrap.appendChild(snrLabel);
|
||||
wrap.appendChild(bar);
|
||||
wrap.appendChild(prn);
|
||||
container.appendChild(wrap);
|
||||
});
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Cleanup
|
||||
// ========================
|
||||
|
||||
|
||||
// ========================
|
||||
// Signal Strength Bars
|
||||
// ========================
|
||||
|
||||
function drawSignalBars(satellites) {
|
||||
const container = document.getElementById('gpsSignalBars');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
if (satellites.length === 0) return;
|
||||
|
||||
// Sort: used first, then by PRN
|
||||
const sorted = [...satellites].sort((a, b) => {
|
||||
if (a.used !== b.used) return a.used ? -1 : 1;
|
||||
return a.prn - b.prn;
|
||||
});
|
||||
|
||||
const maxSnr = 50; // dB-Hz typical max for display
|
||||
|
||||
sorted.forEach(sat => {
|
||||
const snr = sat.snr || 0;
|
||||
const heightPct = Math.min(snr / maxSnr * 100, 100);
|
||||
const color = CONST_COLORS[sat.constellation] || CONST_COLORS['GPS'];
|
||||
const constClass = 'gps-const-' + (sat.constellation || 'GPS').toLowerCase();
|
||||
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'gps-signal-bar-wrap';
|
||||
|
||||
const snrLabel = document.createElement('span');
|
||||
snrLabel.className = 'gps-signal-snr';
|
||||
snrLabel.textContent = snr > 0 ? Math.round(snr) : '';
|
||||
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'gps-signal-bar ' + constClass + (sat.used ? '' : ' unused');
|
||||
bar.style.height = Math.max(heightPct, 2) + '%';
|
||||
bar.title = `PRN ${sat.prn} (${sat.constellation}) - ${Math.round(snr)} dB-Hz${sat.used ? ' [USED]' : ''}`;
|
||||
|
||||
const prn = document.createElement('span');
|
||||
prn.className = 'gps-signal-prn';
|
||||
prn.textContent = sat.prn;
|
||||
|
||||
wrap.appendChild(snrLabel);
|
||||
wrap.appendChild(bar);
|
||||
wrap.appendChild(prn);
|
||||
container.appendChild(wrap);
|
||||
});
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Cleanup
|
||||
// ========================
|
||||
|
||||
function destroy() {
|
||||
unsubscribeFromStream();
|
||||
stopSkyPolling();
|
||||
@@ -1548,11 +1548,11 @@ const GPS = (function() {
|
||||
skyRendererInitPromise = null;
|
||||
setSkyCanvasFallbackMode(false);
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
connect: connect,
|
||||
disconnect: disconnect,
|
||||
destroy: destroy,
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
init: init,
|
||||
connect: connect,
|
||||
disconnect: disconnect,
|
||||
destroy: destroy,
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -254,14 +254,14 @@ const SSTV = (function() {
|
||||
|
||||
// Past track (dimmer, solid)
|
||||
issTrackPast = L.polyline([], {
|
||||
color: '#00d4ff',
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff',
|
||||
weight: 1.5,
|
||||
opacity: 0.3,
|
||||
}).addTo(issMap);
|
||||
|
||||
// Future track (brighter, dashed)
|
||||
issTrackLine = L.polyline([], {
|
||||
color: '#00d4ff',
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff',
|
||||
weight: 2,
|
||||
opacity: 0.7,
|
||||
dashArray: '6, 4'
|
||||
|
||||
@@ -499,7 +499,7 @@ const SubGhz = (function() {
|
||||
// Auto-scale low-amplitude noise/signal so activity is visible.
|
||||
const gain = peak > 0 ? Math.min(12, 0.92 / peak) : 1;
|
||||
|
||||
ctx.strokeStyle = '#00d4ff';
|
||||
ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff';
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.beginPath();
|
||||
const n = data.length;
|
||||
@@ -1779,7 +1779,7 @@ const SubGhz = (function() {
|
||||
|
||||
// Spectrum line
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = '#00d4ff';
|
||||
ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff';
|
||||
ctx.lineWidth = 1.5;
|
||||
|
||||
for (let i = 0; i < sweepData.length; i++) {
|
||||
|
||||
@@ -133,8 +133,8 @@ function plotReceiversOnMap(receivers) {
|
||||
|
||||
const marker = L.circleMarker([rx.lat, rx.lon], {
|
||||
radius: 6,
|
||||
fillColor: rx.available ? '#00d4ff' : '#666',
|
||||
color: rx.available ? '#00d4ff' : '#666',
|
||||
fillColor: rx.available ? (getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff') : '#666',
|
||||
color: rx.available ? (getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff') : '#666',
|
||||
weight: 1,
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.6,
|
||||
@@ -146,7 +146,7 @@ function plotReceiversOnMap(receivers) {
|
||||
${rx.location ? `<span style="color: #aaa;">${escapeHtmlWebsdr(rx.location)}</span><br>` : ''}
|
||||
<span style="color: #888;">Antenna: ${escapeHtmlWebsdr(rx.antenna || 'Unknown')}</span><br>
|
||||
<span style="color: #888;">Users: ${rx.users}/${rx.users_max}</span><br>
|
||||
<button onclick="selectReceiver(${idx})" style="margin-top: 6px; padding: 4px 12px; background: #00d4ff; color: #000; border: none; border-radius: 3px; cursor: pointer; font-weight: bold;">Listen</button>
|
||||
<button onclick="selectReceiver(${idx})" style="margin-top: 6px; padding: 4px 12px; background: var(--accent-cyan); color: #000; border: none; border-radius: 3px; cursor: pointer; font-weight: bold;">Listen</button>
|
||||
</div>
|
||||
`);
|
||||
|
||||
@@ -263,11 +263,12 @@ function initWebsdrGlobe(mapEl) {
|
||||
mapEl.style.background = 'radial-gradient(circle at 30% 20%, rgba(14, 42, 68, 0.9), rgba(4, 9, 16, 0.95) 58%, rgba(2, 4, 9, 0.98) 100%)';
|
||||
mapEl.style.cursor = 'grab';
|
||||
|
||||
const _wsdrAccent = getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#3bb9ff';
|
||||
websdrGlobe = window.Globe()(mapEl)
|
||||
.backgroundColor('rgba(0,0,0,0)')
|
||||
.globeImageUrl(WEBSDR_GLOBE_TEXTURE_URL)
|
||||
.showAtmosphere(true)
|
||||
.atmosphereColor('#3bb9ff')
|
||||
.atmosphereColor(_wsdrAccent)
|
||||
.atmosphereAltitude(0.17)
|
||||
.pointRadius('radius')
|
||||
.pointAltitude('altitude')
|
||||
@@ -396,7 +397,7 @@ function plotReceiversOnGlobe(receivers) {
|
||||
receiverIndex: idx,
|
||||
radius: selected ? 0.52 : 0.38,
|
||||
altitude: selected ? 0.1 : 0.04,
|
||||
color: selected ? '#00ff88' : (rx.available ? '#00d4ff' : '#5f6976'),
|
||||
color: selected ? '#00ff88' : (rx.available ? (getComputedStyle(document.documentElement).getPropertyValue('--accent-cyan').trim() || '#00d4ff') : '#5f6976'),
|
||||
label: buildWebsdrPointLabel(rx, idx),
|
||||
});
|
||||
});
|
||||
@@ -513,7 +514,7 @@ function showWebsdrGlobePopup(point, event) {
|
||||
${rx.location ? `<div style="font-size: 10px; color: var(--text-secondary); margin-bottom: 3px;">${escapeHtmlWebsdr(rx.location)}</div>` : ''}
|
||||
<div style="font-size: 10px; color: var(--text-muted); margin-bottom: 2px;">Antenna: ${escapeHtmlWebsdr(rx.antenna || 'Unknown')}</div>
|
||||
<div style="font-size: 10px; color: var(--text-muted); margin-bottom: 10px;">Users: ${rx.users}/${rx.users_max}</div>
|
||||
<button type="button" data-websdr-listen style="width: 100%; padding: 5px 10px; background: #00d4ff; color: #041018; border: none; border-radius: 4px; cursor: pointer; font-weight: 700;">Listen</button>
|
||||
<button type="button" data-websdr-listen style="width: 100%; padding: 5px 10px; background: var(--accent-cyan); color: #041018; border: none; border-radius: 4px; cursor: pointer; font-weight: 700;">Listen</button>
|
||||
`;
|
||||
websdrGlobePopup.style.display = 'block';
|
||||
|
||||
@@ -551,8 +552,8 @@ function buildWebsdrPointLabel(rx, idx) {
|
||||
const location = rx.location ? escapeHtmlWebsdr(rx.location) : 'Unknown location';
|
||||
const antenna = escapeHtmlWebsdr(rx.antenna || 'Unknown antenna');
|
||||
return `
|
||||
<div style="padding: 4px 6px; font-size: 11px; background: rgba(4, 12, 19, 0.9); border: 1px solid rgba(0,212,255,0.28); border-radius: 4px;">
|
||||
<div style="color: #00d4ff; font-weight: 600;">${escapeHtmlWebsdr(rx.name)}</div>
|
||||
<div style="padding: 4px 6px; font-size: 11px; background: rgba(4, 12, 19, 0.9); border: 1px solid var(--border-glow); border-radius: 4px;">
|
||||
<div style="color: var(--accent-cyan); font-weight: 600;">${escapeHtmlWebsdr(rx.name)}</div>
|
||||
<div style="color: #a5b1c3;">${location}</div>
|
||||
<div style="color: #8f9fb3;">${antenna} · ${rx.users}/${rx.users_max}</div>
|
||||
<div style="color: #7a899b; margin-top: 2px;">Receiver #${idx + 1}</div>
|
||||
|
||||
Reference in New Issue
Block a user