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:
Smittix
2026-02-25 23:26:47 +00:00
parent 8a46293e5c
commit dc7c05b03f
5 changed files with 97 additions and 58 deletions
+4
View File
@@ -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 {
+3 -12
View File
@@ -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;
+28 -7
View File
@@ -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) {