mirror of
https://github.com/smittix/intercept.git
synced 2026-06-08 22:21:55 -07:00
Fix welcome dashboard jitter and refine Morse mode UI
Fix "What's New" section shifting up/down on smaller screens (#157) by isolating the logo pulse animation to its own compositing layer, stabilizing the scrollbar gutter, and pinning the welcome container dimensions. Morse mode improvements: relocate scope and decoded output panels to the main content area, use shared SDR device controls, and reduce panel heights for better layout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -206,6 +206,8 @@ body {
|
||||
max-width: 900px;
|
||||
z-index: 1;
|
||||
animation: welcomeFadeIn 0.8s ease-out;
|
||||
max-height: calc(100vh - 40px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes welcomeFadeIn {
|
||||
@@ -232,6 +234,7 @@ body {
|
||||
|
||||
.welcome-logo {
|
||||
animation: logoPulse 3s ease-in-out infinite;
|
||||
will-change: filter;
|
||||
}
|
||||
|
||||
@keyframes logoPulse {
|
||||
@@ -332,6 +335,7 @@ body {
|
||||
padding: 20px;
|
||||
max-height: calc(100vh - 300px);
|
||||
overflow-y: auto;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.changelog-release {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
.morse-scope-container canvas {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
height: 80px;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -21,8 +21,8 @@
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
min-height: 200px;
|
||||
padding: 12px;
|
||||
min-height: 120px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
font-family: var(--font-mono);
|
||||
@@ -30,7 +30,6 @@
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
word-wrap: break-word;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.morse-decoded-panel:empty::before {
|
||||
@@ -112,14 +111,6 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* Visuals container layout */
|
||||
#morseVisuals {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Word space styling */
|
||||
.morse-word-space {
|
||||
display: inline;
|
||||
|
||||
@@ -67,11 +67,11 @@ var MorseMode = (function () {
|
||||
frequency: document.getElementById('morseFrequency').value || '14.060',
|
||||
gain: document.getElementById('morseGain').value || '0',
|
||||
ppm: document.getElementById('morsePPM').value || '0',
|
||||
device: document.getElementById('morseDevice').value || '0',
|
||||
sdr_type: document.getElementById('morseSdrType').value || 'rtlsdr',
|
||||
device: document.getElementById('deviceSelect')?.value || '0',
|
||||
sdr_type: document.getElementById('sdrTypeSelect')?.value || 'rtlsdr',
|
||||
tone_freq: document.getElementById('morseToneFreq').value || '700',
|
||||
wpm: document.getElementById('morseWpm').value || '15',
|
||||
bias_t: document.getElementById('morseBiasT').checked,
|
||||
bias_t: typeof getBiasTEnabled === 'function' ? getBiasTEnabled() : false,
|
||||
};
|
||||
|
||||
fetch('/morse/start', {
|
||||
@@ -191,6 +191,8 @@ var MorseMode = (function () {
|
||||
// Update count
|
||||
var countEl = document.getElementById('morseCharCount');
|
||||
if (countEl) countEl.textContent = state.charCount + ' chars';
|
||||
var barChars = document.getElementById('morseStatusBarChars');
|
||||
if (barChars) barChars.textContent = state.charCount + ' chars decoded';
|
||||
}
|
||||
|
||||
function appendSpace() {
|
||||
@@ -210,6 +212,8 @@ var MorseMode = (function () {
|
||||
state.decodedLog = [];
|
||||
var countEl = document.getElementById('morseCharCount');
|
||||
if (countEl) countEl.textContent = '0 chars';
|
||||
var barChars = document.getElementById('morseStatusBarChars');
|
||||
if (barChars) barChars.textContent = '0 chars decoded';
|
||||
}
|
||||
|
||||
// ---- Scope canvas ----
|
||||
@@ -221,21 +225,28 @@ var MorseMode = (function () {
|
||||
var dpr = window.devicePixelRatio || 1;
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
canvas.width = rect.width * dpr;
|
||||
canvas.height = 120 * dpr;
|
||||
canvas.style.height = '120px';
|
||||
canvas.height = 80 * dpr;
|
||||
canvas.style.height = '80px';
|
||||
|
||||
scopeCtx = canvas.getContext('2d');
|
||||
scopeCtx.scale(dpr, dpr);
|
||||
scopeHistory = [];
|
||||
|
||||
var toneLabel = document.getElementById('morseScopeToneLabel');
|
||||
var threshLabel = document.getElementById('morseScopeThreshLabel');
|
||||
|
||||
function draw() {
|
||||
if (!scopeCtx) return;
|
||||
var w = rect.width;
|
||||
var h = 120;
|
||||
var h = 80;
|
||||
|
||||
scopeCtx.fillStyle = '#0a0e14';
|
||||
scopeCtx.fillStyle = '#050510';
|
||||
scopeCtx.fillRect(0, 0, w, h);
|
||||
|
||||
// Update header labels
|
||||
if (toneLabel) toneLabel.textContent = scopeToneOn ? 'ON' : '--';
|
||||
if (threshLabel) threshLabel.textContent = scopeThreshold > 0 ? Math.round(scopeThreshold) : '--';
|
||||
|
||||
if (scopeHistory.length === 0) {
|
||||
scopeAnim = requestAnimationFrame(draw);
|
||||
return;
|
||||
@@ -356,6 +367,16 @@ var MorseMode = (function () {
|
||||
if (statusText) {
|
||||
statusText.textContent = running ? 'Listening' : 'Standby';
|
||||
}
|
||||
|
||||
// Toggle scope and output panels (pager/sensor pattern)
|
||||
var scopePanel = document.getElementById('morseScopePanel');
|
||||
var outputPanel = document.getElementById('morseOutputPanel');
|
||||
if (scopePanel) scopePanel.style.display = running ? 'block' : 'none';
|
||||
if (outputPanel) outputPanel.style.display = running ? 'block' : 'none';
|
||||
|
||||
var scopeStatus = document.getElementById('morseScopeStatusLabel');
|
||||
if (scopeStatus) scopeStatus.textContent = running ? 'ACTIVE' : 'IDLE';
|
||||
if (scopeStatus) scopeStatus.style.color = running ? '#0f0' : '#444';
|
||||
}
|
||||
|
||||
function setFreq(mhz) {
|
||||
|
||||
+62
-21
@@ -3008,24 +3008,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Morse Code Decoder Visuals -->
|
||||
<div id="morseVisuals" style="display: none; flex-direction: column; gap: 12px; padding: 16px; height: 100%;">
|
||||
<div class="morse-toolbar">
|
||||
<button class="btn btn-sm btn-outline" onclick="MorseMode.exportTxt()">Export TXT</button>
|
||||
<button class="btn btn-sm btn-outline" onclick="MorseMode.exportCsv()">Export CSV</button>
|
||||
<button class="btn btn-sm btn-outline" id="morseCopyBtn" onclick="MorseMode.copyToClipboard()">Copy</button>
|
||||
<button class="btn btn-sm btn-outline" onclick="MorseMode.clearText()">Clear</button>
|
||||
</div>
|
||||
<div class="morse-scope-container">
|
||||
<canvas id="morseScopeCanvas"></canvas>
|
||||
</div>
|
||||
<div id="morseDecodedText" class="morse-decoded-panel"></div>
|
||||
<div class="morse-status-bar">
|
||||
<span class="status-item" id="morseStatusBarWpm">15 WPM</span>
|
||||
<span class="status-item" id="morseStatusBarTone">700 Hz</span>
|
||||
<span class="status-item" id="morseStatusBarChars">0 chars decoded</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Device Intelligence Dashboard (above waterfall for prominence) -->
|
||||
<div class="recon-panel collapsed" id="reconPanel">
|
||||
@@ -3082,6 +3064,42 @@
|
||||
|
||||
<div id="sensorTimelineContainer" style="display: none; margin-bottom: 12px;"></div>
|
||||
|
||||
<!-- Morse Signal Scope -->
|
||||
<div id="morseScopePanel" style="display: none; margin-bottom: 12px;">
|
||||
<div style="background: #0a0a0a; border: 1px solid #1a2e1a; border-radius: 6px; padding: 8px 10px; font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; font-size: 10px; color: #555; text-transform: uppercase; letter-spacing: 1px;">
|
||||
<span>CW Tone Detection</span>
|
||||
<div style="display: flex; gap: 14px;">
|
||||
<span>TONE: <span id="morseScopeToneLabel" style="color: #0f0; font-variant-numeric: tabular-nums;">--</span></span>
|
||||
<span>THRESH: <span id="morseScopeThreshLabel" style="color: #fa0; font-variant-numeric: tabular-nums;">--</span></span>
|
||||
<span id="morseScopeStatusLabel" style="color: #444;">IDLE</span>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="morseScopeCanvas" style="width: 100%; height: 80px; display: block; border-radius: 3px; background: #050510;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Morse Decoded Output -->
|
||||
<div id="morseOutputPanel" style="display: none; margin-bottom: 12px;">
|
||||
<div style="background: #0a0a0a; border: 1px solid #1a2e1a; border-radius: 6px; padding: 8px 10px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; font-size: 10px; color: #555; text-transform: uppercase; letter-spacing: 1px;">
|
||||
<span>Decoded Text</span>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<button class="btn btn-sm btn-ghost" onclick="MorseMode.exportTxt()">TXT</button>
|
||||
<button class="btn btn-sm btn-ghost" onclick="MorseMode.exportCsv()">CSV</button>
|
||||
<button class="btn btn-sm btn-ghost" id="morseCopyBtn" onclick="MorseMode.copyToClipboard()">Copy</button>
|
||||
<button class="btn btn-sm btn-ghost" onclick="MorseMode.clearText()">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="morseDecodedText" class="morse-decoded-panel"></div>
|
||||
<div class="morse-status-bar">
|
||||
<span class="status-item" id="morseStatusBarWpm">15 WPM</span>
|
||||
<span class="status-item" id="morseStatusBarTone">700 Hz</span>
|
||||
<span class="status-item" id="morseStatusBarChars">0 chars decoded</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-content signal-feed" id="output">
|
||||
<div class="placeholder signal-empty-state">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
@@ -4064,6 +4082,7 @@
|
||||
document.getElementById('subghzMode')?.classList.toggle('active', mode === 'subghz');
|
||||
document.getElementById('spaceWeatherMode')?.classList.toggle('active', mode === 'spaceweather');
|
||||
document.getElementById('waterfallMode')?.classList.toggle('active', mode === 'waterfall');
|
||||
document.getElementById('morseMode')?.classList.toggle('active', mode === 'morse');
|
||||
|
||||
|
||||
const pagerStats = document.getElementById('pagerStats');
|
||||
@@ -4105,7 +4124,6 @@
|
||||
const wefaxVisuals = document.getElementById('wefaxVisuals');
|
||||
const spaceWeatherVisuals = document.getElementById('spaceWeatherVisuals');
|
||||
const waterfallVisuals = document.getElementById('waterfallVisuals');
|
||||
const morseVisuals = document.getElementById('morseVisuals');
|
||||
if (wifiLayoutContainer) wifiLayoutContainer.style.display = mode === 'wifi' ? 'flex' : 'none';
|
||||
if (btLayoutContainer) btLayoutContainer.style.display = mode === 'bluetooth' ? 'flex' : 'none';
|
||||
if (satelliteVisuals) satelliteVisuals.style.display = mode === 'satellite' ? 'block' : 'none';
|
||||
@@ -4123,7 +4141,6 @@
|
||||
if (wefaxVisuals) wefaxVisuals.style.display = mode === 'wefax' ? 'flex' : 'none';
|
||||
if (spaceWeatherVisuals) spaceWeatherVisuals.style.display = mode === 'spaceweather' ? 'flex' : 'none';
|
||||
if (waterfallVisuals) waterfallVisuals.style.display = mode === 'waterfall' ? 'flex' : 'none';
|
||||
if (morseVisuals) morseVisuals.style.display = mode === 'morse' ? 'flex' : 'none';
|
||||
|
||||
// Prevent Leaflet heatmap redraws on hidden BT Locate map containers.
|
||||
if (typeof BtLocate !== 'undefined' && BtLocate.setActiveMode) {
|
||||
@@ -4145,6 +4162,10 @@
|
||||
const sensorTimelineContainer = document.getElementById('sensorTimelineContainer');
|
||||
if (pagerTimelineContainer) pagerTimelineContainer.style.display = mode === 'pager' ? 'block' : 'none';
|
||||
if (sensorTimelineContainer) sensorTimelineContainer.style.display = mode === 'sensor' ? 'block' : 'none';
|
||||
const morseScopePanel = document.getElementById('morseScopePanel');
|
||||
const morseOutputPanel = document.getElementById('morseOutputPanel');
|
||||
if (morseScopePanel && mode !== 'morse') morseScopePanel.style.display = 'none';
|
||||
if (morseOutputPanel && mode !== 'morse') morseOutputPanel.style.display = 'none';
|
||||
|
||||
// Update output panel title based on mode
|
||||
const outputTitle = document.getElementById('outputTitle');
|
||||
@@ -4198,7 +4219,27 @@
|
||||
|
||||
// Show RTL-SDR device section for modes that use it
|
||||
const rtlDeviceSection = document.getElementById('rtlDeviceSection');
|
||||
if (rtlDeviceSection) rtlDeviceSection.style.display = (mode === 'pager' || mode === 'sensor' || mode === 'rtlamr' || mode === 'aprs' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'wefax') ? 'block' : 'none';
|
||||
if (rtlDeviceSection) {
|
||||
rtlDeviceSection.style.display = (mode === 'pager' || mode === 'sensor' || mode === 'rtlamr' || mode === 'aprs' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'wefax' || mode === 'morse') ? 'block' : 'none';
|
||||
// Save original sidebar position of SDR device section (once)
|
||||
if (!rtlDeviceSection._origParent) {
|
||||
rtlDeviceSection._origParent = rtlDeviceSection.parentNode;
|
||||
rtlDeviceSection._origNext = rtlDeviceSection.nextElementSibling;
|
||||
}
|
||||
// For morse mode, move SDR device section inside the morse panel after the title
|
||||
const morsePanel = document.getElementById('morseMode');
|
||||
if (mode === 'morse' && morsePanel) {
|
||||
const firstSection = morsePanel.querySelector('.section');
|
||||
if (firstSection) firstSection.after(rtlDeviceSection);
|
||||
} else if (rtlDeviceSection._origParent && rtlDeviceSection.parentNode !== rtlDeviceSection._origParent) {
|
||||
// Restore to original sidebar position when leaving morse mode
|
||||
if (rtlDeviceSection._origNext) {
|
||||
rtlDeviceSection._origNext.before(rtlDeviceSection);
|
||||
} else {
|
||||
rtlDeviceSection._origParent.appendChild(rtlDeviceSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle mode-specific tool status displays
|
||||
const toolStatusPager = document.getElementById('toolStatusPager');
|
||||
|
||||
@@ -39,24 +39,6 @@
|
||||
<label>PPM Correction</label>
|
||||
<input type="number" id="morsePPM" value="0" step="1" min="-100" max="100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>SDR Type</label>
|
||||
<select id="morseSdrType">
|
||||
<option value="rtlsdr" selected>RTL-SDR</option>
|
||||
<option value="hackrf">HackRF</option>
|
||||
<option value="limesdr">LimeSDR</option>
|
||||
<option value="airspy">Airspy</option>
|
||||
<option value="sdrplay">SDRPlay</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Device Index</label>
|
||||
<input type="number" id="morseDevice" value="0" step="1" min="0" max="9">
|
||||
</div>
|
||||
<div class="form-group" style="display: flex; align-items: center; gap: 8px;">
|
||||
<input type="checkbox" id="morseBiasT">
|
||||
<label for="morseBiasT" style="margin: 0; cursor: pointer;">Bias-T Power</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
|
||||
Reference in New Issue
Block a user