[fix](trx-frontend-http): clean up bookmark spectrum markers and axis labels
- Canvas: remove clashing ribbon polygon; draw only a dashed amber vertical line at each bookmark frequency - Axis labels: replace clip-path ribbon with inline SVG bookmark icon (amber rectangle with V-notch) + name text; add more padding - DOM rebuild: only rebuild axis spans when the set of visible bookmark IDs changes; always update left positions for smooth pan/zoom Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -4274,37 +4274,17 @@ function drawSpectrum(data) {
|
|||||||
: [];
|
: [];
|
||||||
if (visBookmarks.length > 0) {
|
if (visBookmarks.length > 0) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
// Thin amber vertical line from top of canvas down to the ribbon
|
ctx.strokeStyle = "rgba(246,173,85,0.65)";
|
||||||
const BM_RIBBON_H = 14 * dpr; // height of the bookmark ribbon shape
|
|
||||||
const BM_RIBBON_W = 8 * dpr; // half-width of the ribbon
|
|
||||||
ctx.strokeStyle = "rgba(246,173,85,0.70)";
|
|
||||||
ctx.lineWidth = 1 * dpr;
|
ctx.lineWidth = 1 * dpr;
|
||||||
|
ctx.setLineDash([4 * dpr, 3 * dpr]);
|
||||||
for (const bm of visBookmarks) {
|
for (const bm of visBookmarks) {
|
||||||
const x = hzToX(bm.freq_hz);
|
const x = hzToX(bm.freq_hz);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x, 0);
|
ctx.moveTo(x, 0);
|
||||||
ctx.lineTo(x, H - BM_RIBBON_H);
|
ctx.lineTo(x, H);
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
// Bookmark ribbon shape: rectangle with V-notch cut from the bottom
|
|
||||||
for (const bm of visBookmarks) {
|
|
||||||
const x = hzToX(bm.freq_hz);
|
|
||||||
const top = H - BM_RIBBON_H;
|
|
||||||
const bot = H;
|
|
||||||
const notchDepth = 4 * dpr; // depth of the V notch
|
|
||||||
ctx.fillStyle = "rgba(246,173,85,0.92)";
|
|
||||||
ctx.strokeStyle = "rgba(180,100,20,0.60)";
|
|
||||||
ctx.lineWidth = 0.75 * dpr;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x - BM_RIBBON_W, top);
|
|
||||||
ctx.lineTo(x + BM_RIBBON_W, top);
|
|
||||||
ctx.lineTo(x + BM_RIBBON_W, bot - notchDepth);
|
|
||||||
ctx.lineTo(x, bot); // V notch point
|
|
||||||
ctx.lineTo(x - BM_RIBBON_W, bot - notchDepth);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fill();
|
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
ctx.setLineDash([]);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4326,34 +4306,53 @@ function updateBookmarkAxis(range) {
|
|||||||
const hasVisible = visBookmarks.length > 0;
|
const hasVisible = visBookmarks.length > 0;
|
||||||
axisEl.classList.toggle("bm-axis-visible", hasVisible);
|
axisEl.classList.toggle("bm-axis-visible", hasVisible);
|
||||||
if (freqAxisEl) freqAxisEl.classList.toggle("bm-axis-open", hasVisible);
|
if (freqAxisEl) freqAxisEl.classList.toggle("bm-axis-open", hasVisible);
|
||||||
axisEl.innerHTML = "";
|
|
||||||
if (!hasVisible) return;
|
|
||||||
|
|
||||||
|
if (!hasVisible) {
|
||||||
|
if (axisEl.dataset.bmKey) { axisEl.innerHTML = ""; axisEl.dataset.bmKey = ""; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only rebuild DOM when the set of visible bookmarks changes.
|
||||||
|
// Positions are always updated to handle pan/zoom smoothly.
|
||||||
|
const newKey = visBookmarks.map((b) => b.id).join(",");
|
||||||
|
if (axisEl.dataset.bmKey !== newKey) {
|
||||||
|
axisEl.dataset.bmKey = newKey;
|
||||||
|
axisEl.innerHTML = "";
|
||||||
|
const esc = (s) => String(s)
|
||||||
|
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
for (const bm of visBookmarks) {
|
||||||
|
const span = document.createElement("span");
|
||||||
|
const freqStr = typeof bmFmtFreq === "function"
|
||||||
|
? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz";
|
||||||
|
span.title = bm.name + " \u2014 " + freqStr;
|
||||||
|
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);
|
||||||
|
span.addEventListener("click", () => {
|
||||||
|
if (typeof bmApply === "function") bmApply(bm);
|
||||||
|
});
|
||||||
|
axisEl.appendChild(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always recompute horizontal positions (pan/zoom changes frac every frame).
|
||||||
const axisWidth = axisEl.clientWidth || 0;
|
const axisWidth = axisEl.clientWidth || 0;
|
||||||
const edgePad = 6;
|
const edgePad = 8;
|
||||||
for (const bm of visBookmarks) {
|
const spans = axisEl.querySelectorAll("span");
|
||||||
|
visBookmarks.forEach((bm, i) => {
|
||||||
|
const span = spans[i];
|
||||||
|
if (!span) return;
|
||||||
const frac = (bm.freq_hz - range.visLoHz) / range.visSpanHz;
|
const frac = (bm.freq_hz - range.visLoHz) / range.visSpanHz;
|
||||||
const span = document.createElement("span");
|
|
||||||
span.textContent = bm.name;
|
|
||||||
span.title =
|
|
||||||
bm.name +
|
|
||||||
" \u2014 " +
|
|
||||||
(typeof bmFmtFreq === "function" ? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz");
|
|
||||||
span.dataset.bmId = bm.id;
|
|
||||||
span.addEventListener("click", () => {
|
|
||||||
if (typeof bmApply === "function") bmApply(bm);
|
|
||||||
});
|
|
||||||
axisEl.appendChild(span);
|
|
||||||
if (axisWidth > 0) {
|
if (axisWidth > 0) {
|
||||||
const labelWidth = span.offsetWidth || 0;
|
const lw = span.offsetWidth || 0;
|
||||||
const minCenter = edgePad + labelWidth / 2;
|
const clamped = Math.max(edgePad + lw / 2, Math.min(axisWidth - edgePad - lw / 2, frac * axisWidth));
|
||||||
const maxCenter = axisWidth - edgePad - labelWidth / 2;
|
span.style.left = clamped + "px";
|
||||||
const clampedCenter = Math.max(minCenter, Math.min(maxCenter, frac * axisWidth));
|
|
||||||
span.style.left = clampedCenter + "px";
|
|
||||||
} else {
|
} else {
|
||||||
span.style.left = (frac * 100).toFixed(2) + "%";
|
span.style.left = (frac * 100).toFixed(2) + "%";
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSpectrumFreqAxis(range) {
|
function updateSpectrumFreqAxis(range) {
|
||||||
|
|||||||
@@ -1533,7 +1533,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
transition: height 80ms ease;
|
transition: height 80ms ease;
|
||||||
}
|
}
|
||||||
#spectrum-bookmark-axis.bm-axis-visible {
|
#spectrum-bookmark-axis.bm-axis-visible {
|
||||||
height: 22px;
|
height: 26px;
|
||||||
}
|
}
|
||||||
#spectrum-bookmark-axis span {
|
#spectrum-bookmark-axis span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -1543,22 +1543,25 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 0.66rem;
|
font-size: 0.66rem;
|
||||||
/* amber bookmark-ribbon appearance */
|
|
||||||
background: rgba(246,173,85,0.18);
|
background: rgba(246,173,85,0.18);
|
||||||
color: #c07320;
|
color: #b05e10;
|
||||||
border: 1px solid rgba(246,173,85,0.55);
|
border: 1px solid rgba(246,173,85,0.60);
|
||||||
border-radius: 3px 3px 0 0;
|
border-radius: 3px;
|
||||||
padding: 1px 5px 0;
|
padding: 2px 8px 2px 6px;
|
||||||
/* clip the bottom into a V-notch bookmark shape */
|
max-width: 130px;
|
||||||
clip-path: polygon(0 0, 100% 0, 100% 70%, 50% 100%, 0 70%);
|
|
||||||
max-width: 110px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
line-height: 1.5;
|
line-height: 1.4;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
}
|
}
|
||||||
#spectrum-bookmark-axis span:hover {
|
#spectrum-bookmark-axis span:hover {
|
||||||
background: rgba(246,173,85,0.38);
|
background: rgba(246,173,85,0.35);
|
||||||
color: #7a4a00;
|
color: #7a4000;
|
||||||
|
}
|
||||||
|
.bm-icon-svg path {
|
||||||
|
fill: rgba(246,173,85,0.92);
|
||||||
}
|
}
|
||||||
#spectrum-tooltip {
|
#spectrum-tooltip {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
Reference in New Issue
Block a user