[fix](trx-frontend): fix spectrum screenshot hotkey
Preserve the WebGL drawing buffers used by the spectrum snapshot, flush them before compositing, and move the shortcut listener to capture phase so focused widgets do not swallow it. Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
@@ -339,11 +339,13 @@ const connLostOverlayTitleEl = document.getElementById("conn-lost-overlay-title"
|
|||||||
const connLostOverlaySubEl = document.getElementById("conn-lost-overlay-sub");
|
const connLostOverlaySubEl = document.getElementById("conn-lost-overlay-sub");
|
||||||
const overviewCanvas = document.getElementById("overview-canvas");
|
const overviewCanvas = document.getElementById("overview-canvas");
|
||||||
const signalOverlayCanvas = document.getElementById("signal-overlay-canvas");
|
const signalOverlayCanvas = document.getElementById("signal-overlay-canvas");
|
||||||
|
// Screenshots composite these live WebGL canvases into a PNG.
|
||||||
|
const spectrumSnapshotGlOptions = { alpha: true, preserveDrawingBuffer: true };
|
||||||
const overviewGl = typeof createTrxWebGlRenderer === "function"
|
const overviewGl = typeof createTrxWebGlRenderer === "function"
|
||||||
? createTrxWebGlRenderer(overviewCanvas, { alpha: true })
|
? createTrxWebGlRenderer(overviewCanvas, spectrumSnapshotGlOptions)
|
||||||
: null;
|
: null;
|
||||||
const signalOverlayGl = typeof createTrxWebGlRenderer === "function"
|
const signalOverlayGl = typeof createTrxWebGlRenderer === "function"
|
||||||
? createTrxWebGlRenderer(signalOverlayCanvas, { alpha: true })
|
? createTrxWebGlRenderer(signalOverlayCanvas, spectrumSnapshotGlOptions)
|
||||||
: null;
|
: null;
|
||||||
const signalVisualBlockEl = document.querySelector(".signal-visual-block");
|
const signalVisualBlockEl = document.querySelector(".signal-visual-block");
|
||||||
const signalSplitControlEl = document.getElementById("signal-split-control");
|
const signalSplitControlEl = document.getElementById("signal-split-control");
|
||||||
@@ -7920,7 +7922,7 @@ window.addEventListener("beforeunload", () => {
|
|||||||
// ── Spectrum display ─────────────────────────────────────────────────────────
|
// ── Spectrum display ─────────────────────────────────────────────────────────
|
||||||
const spectrumCanvas = document.getElementById("spectrum-canvas");
|
const spectrumCanvas = document.getElementById("spectrum-canvas");
|
||||||
const spectrumGl = typeof createTrxWebGlRenderer === "function"
|
const spectrumGl = typeof createTrxWebGlRenderer === "function"
|
||||||
? createTrxWebGlRenderer(spectrumCanvas, { alpha: true })
|
? createTrxWebGlRenderer(spectrumCanvas, spectrumSnapshotGlOptions)
|
||||||
: null;
|
: null;
|
||||||
const spectrumDbAxis = document.getElementById("spectrum-db-axis");
|
const spectrumDbAxis = document.getElementById("spectrum-db-axis");
|
||||||
const spectrumFreqAxis = document.getElementById("spectrum-freq-axis");
|
const spectrumFreqAxis = document.getElementById("spectrum-freq-axis");
|
||||||
@@ -9103,6 +9105,16 @@ function buildSpectrumSnapshotCanvas() {
|
|||||||
if (!rootEl || !isVisibleForSnapshot(rootEl) || !isVisibleForSnapshot(spectrumPanelEl)) {
|
if (!rootEl || !isVisibleForSnapshot(rootEl) || !isVisibleForSnapshot(spectrumPanelEl)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
for (const renderer of [overviewGl, spectrumGl, signalOverlayGl]) {
|
||||||
|
const gl = renderer?.gl;
|
||||||
|
if (!gl) continue;
|
||||||
|
try {
|
||||||
|
if (typeof gl.flush === "function") gl.flush();
|
||||||
|
if (typeof gl.finish === "function") gl.finish();
|
||||||
|
} catch (_) {
|
||||||
|
// Ignore transient WebGL state errors and capture the last good frame.
|
||||||
|
}
|
||||||
|
}
|
||||||
const rootRect = rootEl.getBoundingClientRect();
|
const rootRect = rootEl.getBoundingClientRect();
|
||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
const out = document.createElement("canvas");
|
const out = document.createElement("canvas");
|
||||||
@@ -9153,35 +9165,55 @@ function buildSpectrumSnapshotCanvas() {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveCanvasAsPng(canvas, fileName) {
|
function clickCanvasDownload(href, fileName) {
|
||||||
if (!canvas) return;
|
|
||||||
if (typeof canvas.toBlob === "function") {
|
|
||||||
canvas.toBlob((blob) => {
|
|
||||||
if (!blob) return;
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = url;
|
|
||||||
a.download = fileName;
|
|
||||||
a.click();
|
|
||||||
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
||||||
}, "image/png");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = canvas.toDataURL("image/png");
|
a.href = href;
|
||||||
a.download = fileName;
|
a.download = fileName;
|
||||||
|
a.rel = "noopener";
|
||||||
|
a.style.display = "none";
|
||||||
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
requestAnimationFrame(() => a.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
function captureSpectrumScreenshot() {
|
function saveCanvasAsPng(canvas, fileName) {
|
||||||
|
if (!canvas) return Promise.resolve(false);
|
||||||
|
if (typeof canvas.toBlob === "function") {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
try {
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
clickCanvasDownload(url, fileName);
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
||||||
|
resolve(true);
|
||||||
|
}, "image/png");
|
||||||
|
} catch (_) {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
clickCanvasDownload(canvas.toDataURL("image/png"), fileName);
|
||||||
|
return Promise.resolve(true);
|
||||||
|
} catch (_) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function captureSpectrumScreenshot() {
|
||||||
const snapshotCanvas = buildSpectrumSnapshotCanvas();
|
const snapshotCanvas = buildSpectrumSnapshotCanvas();
|
||||||
if (!snapshotCanvas) {
|
if (!snapshotCanvas) {
|
||||||
showHint("Spectrum view not ready", 1300);
|
showHint("Spectrum view not ready", 1300);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||||
saveCanvasAsPng(snapshotCanvas, `trx-spectrum-${stamp}.png`);
|
const saved = await saveCanvasAsPng(snapshotCanvas, `trx-spectrum-${stamp}.png`);
|
||||||
showHint("Spectrum screenshot saved", 1500);
|
showHint(saved ? "Spectrum screenshot saved" : "Spectrum screenshot failed", saved ? 1500 : 1800);
|
||||||
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldIgnoreGlobalShortcut(target) {
|
function shouldIgnoreGlobalShortcut(target) {
|
||||||
@@ -9193,13 +9225,13 @@ function shouldIgnoreGlobalShortcut(target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("keydown", (event) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
if (event.defaultPrevented || event.repeat) return;
|
if (event.defaultPrevented || event.repeat || event.isComposing) return;
|
||||||
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
||||||
if (shouldIgnoreGlobalShortcut(event.target)) return;
|
if (shouldIgnoreGlobalShortcut(event.target)) return;
|
||||||
if ((event.key || "").toLowerCase() !== "s") return;
|
if ((event.key || "").toLowerCase() !== "s") return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
captureSpectrumScreenshot();
|
void captureSpectrumScreenshot();
|
||||||
});
|
}, { capture: true });
|
||||||
|
|
||||||
// ── Zoom helpers ──────────────────────────────────────────────────────────────
|
// ── Zoom helpers ──────────────────────────────────────────────────────────────
|
||||||
function spectrumZoomAt(cssX, cssW, data, factor) {
|
function spectrumZoomAt(cssX, cssW, data, factor) {
|
||||||
|
|||||||
Reference in New Issue
Block a user