[feat](trx-frontend): extend spectrum bookmark navigation
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -4854,14 +4854,63 @@ function bmCategoryColorMap() {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createBookmarkChip(bm, colorMap) {
|
||||||
|
const span = document.createElement("span");
|
||||||
|
const freqStr = typeof bmFmtFreq === "function"
|
||||||
|
? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz";
|
||||||
|
const esc = (s) => String(s)
|
||||||
|
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
span.className = "spectrum-bookmark-chip";
|
||||||
|
span.title = bm.name + " \u2014 " + freqStr + (bm.comment ? "\n" + bm.comment : "");
|
||||||
|
span.dataset.bmId = bm.id;
|
||||||
|
span.innerHTML =
|
||||||
|
"<svg class='bm-icon-svg' viewBox='0 0 8 12' width='8' height='12' aria-hidden='true'>" +
|
||||||
|
"<path d='M0,0 h8 v10 l-4,2 l-4,-2 Z'/>" +
|
||||||
|
"</svg>\u00a0" + esc(bm.name);
|
||||||
|
const col = colorMap[bm.category || ""];
|
||||||
|
span.style.setProperty("--bm-cat-bg", col);
|
||||||
|
span.style.setProperty("--bm-cat-fg", bmContrastFg(col));
|
||||||
|
span.addEventListener("click", () => {
|
||||||
|
if (typeof bmApply === "function") bmApply(bm);
|
||||||
|
});
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSideBookmarkStack(container, bookmarks, colorMap) {
|
||||||
|
if (!container) return;
|
||||||
|
container.innerHTML = "";
|
||||||
|
if (!Array.isArray(bookmarks) || bookmarks.length === 0) {
|
||||||
|
container.classList.remove("bm-side-visible");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.classList.add("bm-side-visible");
|
||||||
|
for (const bm of bookmarks) {
|
||||||
|
container.appendChild(createBookmarkChip(bm, colorMap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateBookmarkAxis(range) {
|
function updateBookmarkAxis(range) {
|
||||||
const axisEl = document.getElementById("spectrum-bookmark-axis");
|
const axisEl = document.getElementById("spectrum-bookmark-axis");
|
||||||
|
const leftSideEl = document.getElementById("spectrum-bookmark-side-left");
|
||||||
|
const rightSideEl = document.getElementById("spectrum-bookmark-side-right");
|
||||||
if (!axisEl) return;
|
if (!axisEl) return;
|
||||||
|
|
||||||
const _bmRef = typeof bmList !== "undefined" ? bmList : null;
|
const _bmRef = typeof bmList !== "undefined" ? bmList : null;
|
||||||
const visBookmarks = Array.isArray(_bmRef)
|
const allBookmarks = Array.isArray(_bmRef) ? _bmRef : [];
|
||||||
? _bmRef.filter((bm) => bm.freq_hz >= range.visLoHz && bm.freq_hz <= range.visHiHz)
|
const visBookmarks = allBookmarks.filter((bm) => bm.freq_hz >= range.visLoHz && bm.freq_hz <= range.visHiHz);
|
||||||
: [];
|
const leftBookmarks = allBookmarks
|
||||||
|
.filter((bm) => bm.freq_hz < range.visLoHz)
|
||||||
|
.sort((a, b) => b.freq_hz - a.freq_hz)
|
||||||
|
.slice(0, 3);
|
||||||
|
const rightBookmarks = allBookmarks
|
||||||
|
.filter((bm) => bm.freq_hz > range.visHiHz)
|
||||||
|
.sort((a, b) => a.freq_hz - b.freq_hz)
|
||||||
|
.slice(0, 3);
|
||||||
|
const colorMap = bmCategoryColorMap();
|
||||||
|
|
||||||
|
updateSideBookmarkStack(leftSideEl, leftBookmarks, colorMap);
|
||||||
|
updateSideBookmarkStack(rightSideEl, rightBookmarks, colorMap);
|
||||||
|
|
||||||
const hasVisible = visBookmarks.length > 0;
|
const hasVisible = visBookmarks.length > 0;
|
||||||
axisEl.classList.toggle("bm-axis-visible", hasVisible);
|
axisEl.classList.toggle("bm-axis-visible", hasVisible);
|
||||||
@@ -4877,26 +4926,8 @@ function updateBookmarkAxis(range) {
|
|||||||
if (axisEl.dataset.bmKey !== newKey) {
|
if (axisEl.dataset.bmKey !== newKey) {
|
||||||
axisEl.dataset.bmKey = newKey;
|
axisEl.dataset.bmKey = newKey;
|
||||||
axisEl.innerHTML = "";
|
axisEl.innerHTML = "";
|
||||||
const colorMap = bmCategoryColorMap();
|
|
||||||
const esc = (s) => String(s)
|
|
||||||
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
||||||
for (const bm of visBookmarks) {
|
for (const bm of visBookmarks) {
|
||||||
const span = document.createElement("span");
|
axisEl.appendChild(createBookmarkChip(bm, colorMap));
|
||||||
const freqStr = typeof bmFmtFreq === "function"
|
|
||||||
? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz";
|
|
||||||
span.title = bm.name + " \u2014 " + freqStr + (bm.comment ? "\n" + bm.comment : "");
|
|
||||||
span.dataset.bmId = bm.id;
|
|
||||||
span.innerHTML =
|
|
||||||
"<svg class='bm-icon-svg' viewBox='0 0 8 12' width='8' height='12' aria-hidden='true'>" +
|
|
||||||
"<path d='M0,0 h8 v10 l-4,2 l-4,-2 Z'/>" +
|
|
||||||
"</svg>\u00a0" + esc(bm.name);
|
|
||||||
const col = colorMap[bm.category || ""];
|
|
||||||
span.style.setProperty("--bm-cat-bg", col);
|
|
||||||
span.style.setProperty("--bm-cat-fg", bmContrastFg(col));
|
|
||||||
span.addEventListener("click", () => {
|
|
||||||
if (typeof bmApply === "function") bmApply(bm);
|
|
||||||
});
|
|
||||||
axisEl.appendChild(span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,9 +81,11 @@
|
|||||||
<div id="spectrum-bookmark-axis"></div>
|
<div id="spectrum-bookmark-axis"></div>
|
||||||
<div id="spectrum-panel" style="display:none;">
|
<div id="spectrum-panel" style="display:none;">
|
||||||
<div class="spectrum-wrap">
|
<div class="spectrum-wrap">
|
||||||
|
<div id="spectrum-bookmark-side-left" class="spectrum-bookmark-side spectrum-bookmark-side-left" aria-hidden="true"></div>
|
||||||
<button id="spectrum-center-left-btn" class="spectrum-edge-shift spectrum-edge-shift-left" type="button" aria-label="Shift spectrum center left">‹</button>
|
<button id="spectrum-center-left-btn" class="spectrum-edge-shift spectrum-edge-shift-left" type="button" aria-label="Shift spectrum center left">‹</button>
|
||||||
<canvas id="spectrum-canvas"></canvas>
|
<canvas id="spectrum-canvas"></canvas>
|
||||||
<button id="spectrum-center-right-btn" class="spectrum-edge-shift spectrum-edge-shift-right" type="button" aria-label="Shift spectrum center right">›</button>
|
<button id="spectrum-center-right-btn" class="spectrum-edge-shift spectrum-edge-shift-right" type="button" aria-label="Shift spectrum center right">›</button>
|
||||||
|
<div id="spectrum-bookmark-side-right" class="spectrum-bookmark-side spectrum-bookmark-side-right" aria-hidden="true"></div>
|
||||||
<div id="spectrum-tooltip"></div>
|
<div id="spectrum-tooltip"></div>
|
||||||
<div id="spectrum-freq-axis"></div>
|
<div id="spectrum-freq-axis"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -525,8 +525,10 @@ small { color: var(--text-muted); }
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
margin-bottom: 0.9rem;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
#signal-overlay-canvas {
|
#signal-overlay-canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -912,7 +914,12 @@ small { color: var(--text-muted); }
|
|||||||
.meter-bar { flex: 1 1 auto; height: 12px; border-radius: 999px; background: var(--btn-bg); border: 1px solid var(--border-light); overflow: hidden; }
|
.meter-bar { flex: 1 1 auto; height: 12px; border-radius: 999px; background: var(--btn-bg); border: 1px solid var(--border-light); overflow: hidden; }
|
||||||
.meter-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent-green), var(--accent-yellow), var(--accent-red)); transition: width 150ms ease; }
|
.meter-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent-green), var(--accent-yellow), var(--accent-red)); transition: width 150ms ease; }
|
||||||
.meter-value { font-size: 0.95rem; color: var(--text-heading); min-width: 64px; text-align: right; }
|
.meter-value { font-size: 0.95rem; color: var(--text-heading); min-width: 64px; text-align: right; }
|
||||||
#content { display: flex; flex-direction: column; gap: 1.1rem; min-height: 0; overflow: visible; }
|
#tab-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
#content { display: flex; flex: 1 1 auto; flex-direction: column; gap: 1.1rem; min-height: 0; overflow: visible; }
|
||||||
.tab-panel { flex: 1 1 auto; min-height: 0; overflow: visible; }
|
.tab-panel { flex: 1 1 auto; min-height: 0; overflow: visible; }
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1394,6 +1401,20 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 2.35rem;
|
height: 2.35rem;
|
||||||
}
|
}
|
||||||
|
.spectrum-bookmark-side {
|
||||||
|
width: 5.85rem;
|
||||||
|
gap: 0.22rem;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side-left {
|
||||||
|
left: -7.85rem;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side-right {
|
||||||
|
right: -7.85rem;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side .spectrum-bookmark-chip {
|
||||||
|
font-size: 0.58rem;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
.spectrum-edge-shift-left {
|
.spectrum-edge-shift-left {
|
||||||
left: -1.8rem;
|
left: -1.8rem;
|
||||||
}
|
}
|
||||||
@@ -1493,16 +1514,26 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
/* ── Spectrum display ─────────────────────────────────────────────────── */
|
/* ── Spectrum display ─────────────────────────────────────────────────── */
|
||||||
#spectrum-panel {
|
#spectrum-panel {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.spectrum-wrap {
|
.spectrum-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
#spectrum-canvas {
|
#spectrum-canvas {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: var(--spectrum-plot-height);
|
flex: 1 1 auto;
|
||||||
|
min-height: var(--spectrum-plot-height);
|
||||||
|
height: auto;
|
||||||
background: var(--spectrum-bg);
|
background: var(--spectrum-bg);
|
||||||
border-radius: 6px 6px 0 0;
|
border-radius: 6px 6px 0 0;
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
@@ -1510,7 +1541,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
}
|
}
|
||||||
.spectrum-edge-shift {
|
.spectrum-edge-shift {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(var(--spectrum-plot-height) / 2);
|
top: calc((100% - 18px) / 2);
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
width: 1.7rem;
|
width: 1.7rem;
|
||||||
@@ -1589,7 +1620,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
#spectrum-bookmark-axis.bm-axis-visible {
|
#spectrum-bookmark-axis.bm-axis-visible {
|
||||||
height: 26px;
|
height: 26px;
|
||||||
}
|
}
|
||||||
#spectrum-bookmark-axis span {
|
.spectrum-bookmark-chip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -1610,9 +1641,37 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
#spectrum-bookmark-axis span:hover {
|
.spectrum-bookmark-chip:hover {
|
||||||
filter: brightness(1.15);
|
filter: brightness(1.15);
|
||||||
}
|
}
|
||||||
|
.spectrum-bookmark-side {
|
||||||
|
position: absolute;
|
||||||
|
top: calc((100% - 18px) / 2);
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 7;
|
||||||
|
width: 7.25rem;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.28rem;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side.bm-side-visible {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side-left {
|
||||||
|
left: -10.15rem;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side-right {
|
||||||
|
right: -10.15rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.spectrum-bookmark-side .spectrum-bookmark-chip {
|
||||||
|
position: relative;
|
||||||
|
top: auto;
|
||||||
|
left: auto;
|
||||||
|
transform: none;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
.bm-icon-svg path {
|
.bm-icon-svg path {
|
||||||
fill: var(--bm-cat-fg, #1a202c);
|
fill: var(--bm-cat-fg, #1a202c);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user