diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js index e4bdb127..63f36ff8 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js @@ -488,12 +488,9 @@ function currentDecodeHistoryRetentionMs() { } window.getDecodeHistoryRetentionMs = currentDecodeHistoryRetentionMs; window.applyDecodeHistoryRetention = function() { - window.trxPluginRuntime.prune("aprs"); - window.trxPluginRuntime.prune("hf_aprs"); - window.trxPluginRuntime.prune("ais"); - window.trxPluginRuntime.prune("vdes"); - if (typeof window.pruneFt8HistoryView === "function") window.pruneFt8HistoryView(); - if (typeof window.pruneWsprHistoryView === "function") window.pruneWsprHistoryView(); + for (const decoder of ["aprs", "hf_aprs", "ais", "vdes", "ft8", "ft4", "ft2", "wspr", "wefax"]) { + window.trxPluginRuntime.prune(decoder); + } }; function syncTopBarAccess() { const loggedOut = authEnabled && !authRole; @@ -5857,18 +5854,7 @@ function updateDecodeStatus(text) { } } function dispatchDecodeMessage(msg, skipStats) { - if (msg.type === "ais" || msg.type === "vdes" || msg.type === "aprs" || msg.type === "hf_aprs") { - window.trxPluginRuntime.dispatch(msg.type, msg); - } - if (msg.type === "cw" && window.onServerCw) window.onServerCw(msg); - if (msg.type === "ft8" && window.onServerFt8) window.onServerFt8(msg); - if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg); - if (msg.type === "ft2" && window.onServerFt2) window.onServerFt2(msg); - if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg); - if (msg.type === "lrpt_image" && window.onServerLrptImage) window.onServerLrptImage(msg); - if (msg.type === "lrpt_progress" && window.onServerLrptProgress) window.onServerLrptProgress(msg); - if (msg.type === "wefax" && window.onServerWefax) window.onServerWefax(msg); - if (msg.type === "wefax_progress" && window.onServerWefaxProgress) window.onServerWefaxProgress(msg); + if (msg.type) window.trxPluginRuntime.dispatch(msg.type, msg); if (!skipStats && msg.type && msg.type !== "lrpt_image" && msg.type !== "lrpt_progress" && msg.type !== "wefax" && msg.type !== "wefax_progress") { window.trx.modules.map?.statsRecordDecode(msg.type, msg.rig_id || msg.remote || null); window.trx.modules.map?.scheduleStatsRender(); @@ -5884,27 +5870,9 @@ function dispatchDecodeBatch(batch) { window.trx.modules.map?.scheduleStatsRender(); const type = String(batch[0]?.type || ""); const uniformType = batch.every((msg) => String(msg?.type || "") === type); - if (uniformType) { - if (type === "ais" || type === "vdes" || type === "aprs" || type === "hf_aprs") { - window.trxPluginRuntime.dispatchBatch(type, batch); - return; - } - if (type === "ft8" && window.onServerFt8Batch) { - window.onServerFt8Batch(batch); - return; - } - if (type === "ft4" && window.onServerFt4Batch) { - window.onServerFt4Batch(batch); - return; - } - if (type === "ft2" && window.onServerFt2Batch) { - window.onServerFt2Batch(batch); - return; - } - if (type === "wspr" && window.onServerWsprBatch) { - window.onServerWsprBatch(batch); - return; - } + if (uniformType && type) { + window.trxPluginRuntime.dispatchBatch(type, batch); + return; } for (const msg of batch) { dispatchDecodeMessage(msg, true); @@ -5954,34 +5922,7 @@ function restoreDecodeHistoryGroup(kind, messages) { } window.trx.modules.map?.scheduleStatsRender(); } - if (kind === "ais" || kind === "vdes" || kind === "aprs" || kind === "hf_aprs") { - window.trxPluginRuntime.restore(kind, messages); - return; - } - if (kind === "cw" && window.restoreCwHistory) { - window.restoreCwHistory(messages); - return; - } - if (kind === "ft8" && window.restoreFt8History) { - window.restoreFt8History(messages); - return; - } - if (kind === "ft4" && window.restoreFt4History) { - window.restoreFt4History(messages); - return; - } - if (kind === "ft2" && window.restoreFt2History) { - window.restoreFt2History(messages); - return; - } - if (kind === "wspr" && window.restoreWsprHistory) { - window.restoreWsprHistory(messages); - return; - } - if (kind === "wefax" && window.restoreWefaxHistory) { - window.restoreWefaxHistory(messages); - return; - } + window.trxPluginRuntime.restore(kind, messages); } function connectDecode() { if (decodeSource) { @@ -5991,14 +5932,7 @@ function connectDecode() { decodeHistoryReplayActive = false; decodeMapSyncPending = false; window.trxPluginRuntime.clearQueued(); - window.trxPluginRuntime.reset("ais"); - window.trxPluginRuntime.reset("vdes"); - window.trxPluginRuntime.reset("aprs"); - window.trxPluginRuntime.reset("hf_aprs"); - if (window.resetCwHistoryView) window.resetCwHistoryView(); - if (window.resetFt8HistoryView) window.resetFt8HistoryView(); - if (window.resetFt4HistoryView) window.resetFt4HistoryView(); - if (window.resetWsprHistoryView) window.resetWsprHistoryView(); + window.trxPluginRuntime.resetAll(); let historySettled = false; let historyWorkerDone = false; let historyFallbackStarted = false; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/cw.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/cw.js index d9b8d649..562ae85d 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/cw.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/cw.js @@ -82,7 +82,7 @@ } cwWindow.updateCwBar = updateCwBar; cwWindow.clearCwBar = function() { - cwWindow.resetCwHistoryView?.(); + resetCwHistoryView(); }; cwWindow.closeCwBar = function() { cwBarDismissedAtMs = Date.now(); @@ -302,26 +302,26 @@ void setCwTone(tone); }); } - cwWindow.resetCwHistoryView = function() { + function resetCwHistoryView() { if (cwOutputEl) cwOutputEl.innerHTML = ""; cwLastAppendTime = 0; cwBarHistory = []; cwBarCurrentLine = null; updateCwBar(); drawCwTonePicker(); - }; + } document.getElementById("settings-clear-cw-history")?.addEventListener("click", () => { void (async () => { if (!await cwWindow.trxUi.confirm({ title: "Clear CW history?", message: "All stored CW decodes will be permanently removed.", confirmLabel: "Clear history" })) return; try { await cwWindow.postPath?.("/clear_cw_decode"); - cwWindow.resetCwHistoryView?.(); + resetCwHistoryView(); } catch (error) { console.error("CW history clear failed", error); } })(); }); - cwWindow.onServerCw = function(evt) { + function onServerCw(evt) { if (cwStatusEl) cwStatusEl.textContent = "Receiving"; if (evt.text && cwOutputEl) { const now = Date.now(); @@ -375,14 +375,20 @@ cwTonePickerRaf = null; drawCwTonePicker(); }); - }; - cwWindow.restoreCwHistory = function(events) { + } + function restoreCwHistory(events) { if (!Array.isArray(events) || events.length === 0) return; if (cwStatusEl) cwStatusEl.textContent = "Receiving"; for (const evt of events) { - cwWindow.onServerCw?.(evt); + onServerCw(evt); } - }; + } + cwWindow.trxPluginRuntime.registerDecoder({ + id: "cw", + onMessage: onServerCw, + restore: restoreCwHistory, + reset: resetCwHistoryView + }); cwWindow.refreshCwTonePicker = function refreshCwTonePicker() { ensureCwToneCanvasResolution(); drawCwTonePicker(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft2.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft2.js index bf10f860..f9f1872b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft2.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft2.js @@ -167,19 +167,19 @@ } return { count: recent.length, newestTsMs: recent.reduce((latest, message) => Math.max(latest, finiteNumber(message._tsMs ?? message.ts_ms) ?? 0), 0), html }; }; - const capitalized = `${id[0]?.toUpperCase() ?? ""}${id.slice(1)}`; - bridge[`onServer${capitalized}Batch`] = (messages) => { - if (Array.isArray(messages)) receiveBatch(messages); - }; - bridge[`restore${capitalized}History`] = receiveBatch; - bridge[`prune${capitalized}HistoryView`] = () => { - prune(); - render(); - }; - bridge[`reset${capitalized}HistoryView`] = reset; - bridge[`onServer${capitalized}`] = (message) => { - receiveBatch([message]); - }; + bridge.trxPluginRuntime.registerDecoder({ + id, + onMessage: (message) => { + receiveBatch([message]); + }, + onBatch: receiveBatch, + restore: receiveBatch, + prune: () => { + prune(); + render(); + }, + reset + }); bridge.registerFt8FamilyBarRenderer?.(id, barFrames); const updatePeriod = () => { if (period) period.textContent = `Next slot ${((periodMs - Date.now() % periodMs) / 1e3).toFixed(periodDigits)}s`; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft4.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft4.js index dca9347f..0076c658 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft4.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft4.js @@ -167,19 +167,19 @@ } return { count: recent.length, newestTsMs: recent.reduce((latest, message) => Math.max(latest, finiteNumber(message._tsMs ?? message.ts_ms) ?? 0), 0), html }; }; - const capitalized = `${id[0]?.toUpperCase() ?? ""}${id.slice(1)}`; - bridge[`onServer${capitalized}Batch`] = (messages) => { - if (Array.isArray(messages)) receiveBatch(messages); - }; - bridge[`restore${capitalized}History`] = receiveBatch; - bridge[`prune${capitalized}HistoryView`] = () => { - prune(); - render(); - }; - bridge[`reset${capitalized}HistoryView`] = reset; - bridge[`onServer${capitalized}`] = (message) => { - receiveBatch([message]); - }; + bridge.trxPluginRuntime.registerDecoder({ + id, + onMessage: (message) => { + receiveBatch([message]); + }, + onBatch: receiveBatch, + restore: receiveBatch, + prune: () => { + prune(); + render(); + }, + reset + }); bridge.registerFt8FamilyBarRenderer?.(id, barFrames); const updatePeriod = () => { if (period) period.textContent = `Next slot ${((periodMs - Date.now() % periodMs) / 1e3).toFixed(periodDigits)}s`; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft8.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft8.js index 8aa5c587..8c782734 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft8.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/ft8.js @@ -91,7 +91,7 @@ }; bridge.updateFt8Bar = update; bridge.clearFt8Bar = () => { - ({ ft8: bridge.resetFt8HistoryView, ft4: bridge.resetFt4HistoryView, ft2: bridge.resetFt2HistoryView })[active]?.(); + bridge.trxPluginRuntime.reset(active); }; bridge.closeFt8Bar = () => { dismissed[active] = Date.now(); @@ -209,19 +209,19 @@ } return { count: recent.length, newestTsMs: recent.reduce((latest, message) => Math.max(latest, finiteNumber(message._tsMs ?? message.ts_ms) ?? 0), 0), html }; }; - const capitalized = `${id[0]?.toUpperCase() ?? ""}${id.slice(1)}`; - bridge[`onServer${capitalized}Batch`] = (messages) => { - if (Array.isArray(messages)) receiveBatch(messages); - }; - bridge[`restore${capitalized}History`] = receiveBatch; - bridge[`prune${capitalized}HistoryView`] = () => { - prune(); - render(); - }; - bridge[`reset${capitalized}HistoryView`] = reset; - bridge[`onServer${capitalized}`] = (message) => { - receiveBatch([message]); - }; + bridge.trxPluginRuntime.registerDecoder({ + id, + onMessage: (message) => { + receiveBatch([message]); + }, + onBatch: receiveBatch, + restore: receiveBatch, + prune: () => { + prune(); + render(); + }, + reset + }); bridge.registerFt8FamilyBarRenderer?.(id, barFrames); const updatePeriod = () => { if (period) period.textContent = `Next slot ${((periodMs - Date.now() % periodMs) / 1e3).toFixed(periodDigits)}s`; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat.js index 35800f51..f826701a 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat.js @@ -207,29 +207,39 @@ }); } } - satWindow.onServerLrptProgress = function(msg) { + function onServerLrptProgress(msg) { if (satDom.status && (msg.mcu_count ?? 0) > 0) { satDom.status.textContent = `Receiving — ${String(msg.mcu_count ?? 0)} MCU rows decoded`; } - }; - satWindow.onServerLrptImage = function(msg) { + } + function onServerLrptImage(msg) { if (satDom.status) satDom.status.textContent = "Image received (Meteor LRPT)"; addSatImage(msg, "lrpt"); if (msg.geo_bounds && msg.path && satWindow.addSatMapOverlay) { satWindow.addSatMapOverlay(msg); } - }; - satWindow.resetSatHistoryView = function() { + } + function resetSatHistoryView() { satImageHistory = []; if (satDom.historyList) satDom.historyList.innerHTML = ""; renderSatLatestCard(); renderSatHistoryTable(); satWindow.clearSatMapOverlays?.(); - }; - satWindow.pruneSatHistoryView = function() { + } + function pruneSatHistoryView() { renderSatHistoryTable(); renderSatLatestCard(); - }; + } + satWindow.trxPluginRuntime.registerDecoder({ + id: "lrpt_image", + onMessage: onServerLrptImage, + reset: resetSatHistoryView, + prune: pruneSatHistoryView + }); + satWindow.trxPluginRuntime.registerDecoder({ + id: "lrpt_progress", + onMessage: onServerLrptProgress + }); var lrptDecodeToggleBtn = document.getElementById("lrpt-decode-toggle-btn"); lrptDecodeToggleBtn?.addEventListener("click", () => { void (async () => { @@ -257,7 +267,7 @@ if (!await satWindow.trxUi.confirm({ title: "Clear satellite history?", message: "All stored satellite decodes will be permanently removed.", confirmLabel: "Clear history" })) return; try { await satWindow.postPath?.("/clear_lrpt_decode"); - satWindow.resetSatHistoryView?.(); + resetSatHistoryView(); } catch (e) { console.error("Weather satellite history clear failed", e); } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wefax.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wefax.js index 5e07f93a..f3934838 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wefax.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wefax.js @@ -205,7 +205,7 @@ scheduleWefaxUi("wefax-history", renderWefaxHistoryTable); } } - wefaxWindow.onServerWefaxProgress = function(msg) { + function onServerWefaxProgress(msg) { if (msg.state && !msg.line_data) { if (wefaxDom.status) { wefaxDom.status.textContent = msg.state; @@ -229,16 +229,16 @@ wefaxDom.status.textContent = `Receiving — line ${String(msg.line_count ?? 0)}`; wefaxDom.status.style.color = "var(--text-accent)"; } - }; - wefaxWindow.onServerWefax = function(msg) { + } + function onServerWefax(msg) { addWefaxImage(msg); if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = "none"; if (wefaxDom.status) { wefaxDom.status.textContent = `Complete — ${String(msg.line_count ?? 0)} lines`; wefaxDom.status.style.color = ""; } - }; - wefaxWindow.restoreWefaxHistory = function(messages) { + } + function restoreWefaxHistory(messages) { if (!messages.length) return; for (const message of messages) { const tsMs = Number.isFinite(message.ts_ms) ? Number(message.ts_ms) : Date.now(); @@ -255,13 +255,13 @@ if (wefaxActiveView === "history") { scheduleWefaxUi("wefax-history", renderWefaxHistoryTable); } - }; - wefaxWindow.pruneWefaxHistoryView = function() { + } + function pruneWefaxHistoryView() { pruneWefaxHistory(); renderWefaxHistoryTable(); renderWefaxLatestCard(); - }; - wefaxWindow.resetWefaxHistoryView = function() { + } + function resetWefaxHistoryView() { wefaxImageHistory = []; if (wefaxDom.historyList) wefaxDom.historyList.innerHTML = ""; if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = "none"; @@ -273,7 +273,7 @@ wefaxDom.status.textContent = "Idle"; wefaxDom.status.style.color = ""; } - }; + } if (wefaxDom.filterInput) { const filterInput = wefaxDom.filterInput; wefaxDom.filterInput.addEventListener("input", function() { @@ -313,7 +313,7 @@ void (async () => { try { await wefaxWindow.postPath?.("/clear_wefax_decode"); - wefaxWindow.resetWefaxHistoryView?.(); + resetWefaxHistoryView(); } catch (e) { console.error("WEFAX clear failed", e); } @@ -321,4 +321,15 @@ }); } renderWefaxLatestCard(); + wefaxWindow.trxPluginRuntime.registerDecoder({ + id: "wefax", + onMessage: onServerWefax, + restore: restoreWefaxHistory, + prune: pruneWefaxHistoryView, + reset: resetWefaxHistoryView + }); + wefaxWindow.trxPluginRuntime.registerDecoder({ + id: "wefax_progress", + onMessage: onServerWefaxProgress + }); })(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wspr.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wspr.js index 752fc4dc..81f1ad59 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wspr.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/wspr.js @@ -97,7 +97,7 @@ } }; } - wsprWindow.onServerWsprBatch = function(messages) { + function onServerWsprBatch(messages) { if (!Array.isArray(messages) || messages.length === 0) return; if (wsprStatus) wsprStatus.textContent = "Receiving"; const normalized = []; @@ -116,15 +116,11 @@ wsprMessageHistory = normalized.concat(wsprMessageHistory); pruneWsprMessageHistory(); scheduleWsprHistoryRender(); - }; - wsprWindow.restoreWsprHistory = function(messages) { - const callback = wsprWindow.onServerWsprBatch; - if (typeof callback === "function") callback(messages); - }; - wsprWindow.pruneWsprHistoryView = function() { + } + function pruneWsprHistoryView() { pruneWsprMessageHistory(); renderWsprHistory(); - }; + } function escapeWsprHtml(input) { return input.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """); } @@ -205,12 +201,12 @@ const message = row.dataset.message || ""; row.style.display = message.includes(wsprFilterText) ? "" : "none"; } - wsprWindow.resetWsprHistoryView = function() { + function resetWsprHistoryView() { if (wsprMessagesEl) wsprMessagesEl.innerHTML = ""; wsprMessageHistory = []; renderWsprHistory(); if (wsprWindow.clearMapMarkersByType) wsprWindow.clearMapMarkersByType("wspr"); - }; + } if (wsprFilterInput) { wsprFilterInput.addEventListener("input", () => { wsprFilterText = wsprFilterInput.value.trim().toUpperCase(); @@ -246,13 +242,13 @@ if (!await wsprWindow.trxUi.confirm({ title: "Clear WSPR history?", message: "All stored WSPR decodes will be permanently removed.", confirmLabel: "Clear history" })) return; try { await wsprWindow.postPath?.("/clear_wspr_decode"); - wsprWindow.resetWsprHistoryView?.(); + resetWsprHistoryView(); } catch (error) { console.error("WSPR history clear failed", error); } })(); }); - wsprWindow.onServerWspr = function(msg) { + function onServerWspr(msg) { if (wsprStatus) wsprStatus.textContent = "Receiving"; const next = normalizeServerWsprMessage(msg); if (next.grids.length > 0 && wsprWindow.mapAddLocator) { @@ -262,5 +258,13 @@ }); } addWsprMessage(next.history); - }; + } + wsprWindow.trxPluginRuntime.registerDecoder({ + id: "wspr", + onMessage: onServerWspr, + onBatch: onServerWsprBatch, + restore: onServerWsprBatch, + prune: pruneWsprHistoryView, + reset: resetWsprHistoryView + }); })(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/app.js b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/app.js index de5058db..6da46018 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/app.js @@ -567,12 +567,9 @@ function currentDecodeHistoryRetentionMs() { window.getDecodeHistoryRetentionMs = currentDecodeHistoryRetentionMs; window.applyDecodeHistoryRetention = function() { - window.trxPluginRuntime.prune("aprs"); - window.trxPluginRuntime.prune("hf_aprs"); - window.trxPluginRuntime.prune("ais"); - window.trxPluginRuntime.prune("vdes"); - if (typeof window.pruneFt8HistoryView === "function") window.pruneFt8HistoryView(); - if (typeof window.pruneWsprHistoryView === "function") window.pruneWsprHistoryView(); + for (const decoder of ["aprs", "hf_aprs", "ais", "vdes", "ft8", "ft4", "ft2", "wspr", "wefax"]) { + window.trxPluginRuntime.prune(decoder); + } }; function syncTopBarAccess() { @@ -6215,18 +6212,7 @@ function updateDecodeStatus(text) { } } function dispatchDecodeMessage(msg, skipStats) { - if (msg.type === "ais" || msg.type === "vdes" || msg.type === "aprs" || msg.type === "hf_aprs") { - window.trxPluginRuntime.dispatch(msg.type, msg); - } - if (msg.type === "cw" && window.onServerCw) window.onServerCw(msg); - if (msg.type === "ft8" && window.onServerFt8) window.onServerFt8(msg); - if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg); - if (msg.type === "ft2" && window.onServerFt2) window.onServerFt2(msg); - if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg); - if (msg.type === "lrpt_image" && window.onServerLrptImage) window.onServerLrptImage(msg); - if (msg.type === "lrpt_progress" && window.onServerLrptProgress) window.onServerLrptProgress(msg); - if (msg.type === "wefax" && window.onServerWefax) window.onServerWefax(msg); - if (msg.type === "wefax_progress" && window.onServerWefaxProgress) window.onServerWefaxProgress(msg); + if (msg.type) window.trxPluginRuntime.dispatch(msg.type, msg); if (!skipStats && msg.type && msg.type !== "lrpt_image" && msg.type !== "lrpt_progress" && msg.type !== "wefax" && msg.type !== "wefax_progress") { window.trx.modules.map?.statsRecordDecode(msg.type, msg.rig_id || msg.remote || null); window.trx.modules.map?.scheduleStatsRender(); @@ -6244,27 +6230,9 @@ function dispatchDecodeBatch(batch) { window.trx.modules.map?.scheduleStatsRender(); const type = String(batch[0]?.type || ""); const uniformType = batch.every((msg) => String(msg?.type || "") === type); - if (uniformType) { - if (type === "ais" || type === "vdes" || type === "aprs" || type === "hf_aprs") { - window.trxPluginRuntime.dispatchBatch(type, batch); - return; - } - if (type === "ft8" && window.onServerFt8Batch) { - window.onServerFt8Batch(batch); - return; - } - if (type === "ft4" && window.onServerFt4Batch) { - window.onServerFt4Batch(batch); - return; - } - if (type === "ft2" && window.onServerFt2Batch) { - window.onServerFt2Batch(batch); - return; - } - if (type === "wspr" && window.onServerWsprBatch) { - window.onServerWsprBatch(batch); - return; - } + if (uniformType && type) { + window.trxPluginRuntime.dispatchBatch(type, batch); + return; } for (const msg of batch) { dispatchDecodeMessage(msg, true); @@ -6318,34 +6286,7 @@ function restoreDecodeHistoryGroup(kind, messages) { } window.trx.modules.map?.scheduleStatsRender(); } - if (kind === "ais" || kind === "vdes" || kind === "aprs" || kind === "hf_aprs") { - window.trxPluginRuntime.restore(kind, messages); - return; - } - if (kind === "cw" && window.restoreCwHistory) { - window.restoreCwHistory(messages); - return; - } - if (kind === "ft8" && window.restoreFt8History) { - window.restoreFt8History(messages); - return; - } - if (kind === "ft4" && window.restoreFt4History) { - window.restoreFt4History(messages); - return; - } - if (kind === "ft2" && window.restoreFt2History) { - window.restoreFt2History(messages); - return; - } - if (kind === "wspr" && window.restoreWsprHistory) { - window.restoreWsprHistory(messages); - return; - } - if (kind === "wefax" && window.restoreWefaxHistory) { - window.restoreWefaxHistory(messages); - return; - } + window.trxPluginRuntime.restore(kind, messages); } function connectDecode() { @@ -6354,14 +6295,7 @@ function connectDecode() { decodeHistoryReplayActive = false; decodeMapSyncPending = false; window.trxPluginRuntime.clearQueued(); - window.trxPluginRuntime.reset("ais"); - window.trxPluginRuntime.reset("vdes"); - window.trxPluginRuntime.reset("aprs"); - window.trxPluginRuntime.reset("hf_aprs"); - if (window.resetCwHistoryView) window.resetCwHistoryView(); - if (window.resetFt8HistoryView) window.resetFt8HistoryView(); - if (window.resetFt4HistoryView) window.resetFt4HistoryView(); - if (window.resetWsprHistoryView) window.resetWsprHistoryView(); + window.trxPluginRuntime.resetAll(); // Buffer live messages until history fetch settles so history always appears // before any live updates, regardless of network ordering. diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/cw.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/cw.ts index 404fbf73..a91b9c0e 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/cw.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/cw.ts @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: GPL-2.0-or-later +import type { PluginRuntimeWindow } from "./runtime-contract.js"; + export {}; type Rgba = [number, number, number, number]; @@ -36,12 +38,9 @@ interface CwBridge { updateCwBar?: () => void; clearCwBar?: () => void; closeCwBar?: () => void; - resetCwHistoryView?: () => void; - onServerCw?: (event: CwEvent) => void; - restoreCwHistory?: (events: CwEvent[]) => void; refreshCwTonePicker?: () => void; } -const cwWindow = window as unknown as CwBridge; +const cwWindow = window as unknown as CwBridge & PluginRuntimeWindow; // --- CW (Morse) Decoder Plugin (server-side decode) --- const cwStatusEl = document.getElementById("cw-status"); @@ -154,7 +153,7 @@ function updateCwBar(): void { } cwWindow.updateCwBar = updateCwBar; cwWindow.clearCwBar = function() { - cwWindow.resetCwHistoryView?.(); + resetCwHistoryView(); }; cwWindow.closeCwBar = function() { cwBarDismissedAtMs = Date.now(); @@ -401,21 +400,21 @@ if (cwToneCanvas) { }); } -cwWindow.resetCwHistoryView = function() { +function resetCwHistoryView(): void { if (cwOutputEl) cwOutputEl.innerHTML = ""; cwLastAppendTime = 0; cwBarHistory = []; cwBarCurrentLine = null; updateCwBar(); drawCwTonePicker(); -}; +} document.getElementById("settings-clear-cw-history")?.addEventListener("click", () => { void (async () => { if (!await cwWindow.trxUi.confirm({ title: "Clear CW history?", message: "All stored CW decodes will be permanently removed.", confirmLabel: "Clear history" })) return; try { await cwWindow.postPath?.("/clear_cw_decode"); - cwWindow.resetCwHistoryView?.(); + resetCwHistoryView(); } catch (error: unknown) { console.error("CW history clear failed", error); } @@ -423,7 +422,7 @@ document.getElementById("settings-clear-cw-history")?.addEventListener("click", }); // --- Server-side CW decode handler --- -cwWindow.onServerCw = function(evt: CwEvent) { +function onServerCw(evt: CwEvent): void { if (cwStatusEl) cwStatusEl.textContent = "Receiving"; if (evt.text && cwOutputEl) { // Append decoded text to output @@ -479,15 +478,22 @@ cwWindow.onServerCw = function(evt: CwEvent) { cwTonePickerRaf = null; drawCwTonePicker(); }); -}; +} -cwWindow.restoreCwHistory = function(events: CwEvent[]) { +function restoreCwHistory(events: CwEvent[]): void { if (!Array.isArray(events) || events.length === 0) return; if (cwStatusEl) cwStatusEl.textContent = "Receiving"; for (const evt of events) { - cwWindow.onServerCw?.(evt); + onServerCw(evt); } -}; +} + +cwWindow.trxPluginRuntime.registerDecoder({ + id: "cw", + onMessage: onServerCw, + restore: restoreCwHistory, + reset: resetCwHistoryView, +}); cwWindow.refreshCwTonePicker = function refreshCwTonePicker() { ensureCwToneCanvasResolution(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/ftx-family.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/ftx-family.ts index 31fac660..c0c4f719 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/ftx-family.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/ftx-family.ts @@ -2,9 +2,11 @@ // // SPDX-License-Identifier: GPL-2.0-or-later +import type { PluginRuntimeWindow } from "./runtime-contract.js"; + export type FtxDecoderId = "ft2" | "ft4" | "ft8"; -interface FtxMessage { +export interface FtxMessage { message?: string | undefined; ts_ms?: number | undefined; _tsMs?: number | undefined; @@ -46,9 +48,6 @@ interface FtxBridge { postPath?: (path: string) => Promise; fmtTime?: (timestampMs: number) => string; trxUi: ConfirmApi; - resetFt2HistoryView?: () => void; - resetFt4HistoryView?: () => void; - resetFt8HistoryView?: () => void; clearFt8Bar?: () => void; closeFt8Bar?: () => void; [key: string]: unknown; @@ -61,7 +60,7 @@ interface FtxConfig { periodDigits?: number; } -const bridge = window as unknown as FtxBridge; +const bridge = window as unknown as FtxBridge & PluginRuntimeWindow; function finiteNumber(value: unknown): number | null { const number = typeof value === "number" ? value : Number(value); @@ -164,7 +163,7 @@ export function initializeFt8FamilyBar(): void { bridge.setFt8FamilyBarDecoder = (decoder) => { active = decoder; update(); }; bridge.updateFt8Bar = update; bridge.clearFt8Bar = () => { - ({ ft8: bridge.resetFt8HistoryView, ft4: bridge.resetFt4HistoryView, ft2: bridge.resetFt2HistoryView })[active]?.(); + bridge.trxPluginRuntime.reset(active); }; bridge.closeFt8Bar = () => { dismissed[active] = Date.now(); @@ -285,12 +284,14 @@ export function initializeFtxDecoder(config: FtxConfig): void { return { count: recent.length, newestTsMs: recent.reduce((latest, message) => Math.max(latest, finiteNumber(message._tsMs ?? message.ts_ms) ?? 0), 0), html }; }; - const capitalized = `${id[0]?.toUpperCase() ?? ""}${id.slice(1)}`; - bridge[`onServer${capitalized}Batch`] = (messages: FtxMessage[]) => { if (Array.isArray(messages)) receiveBatch(messages); }; - bridge[`restore${capitalized}History`] = receiveBatch; - bridge[`prune${capitalized}HistoryView`] = () => { prune(); render(); }; - bridge[`reset${capitalized}HistoryView`] = reset; - bridge[`onServer${capitalized}`] = (message: FtxMessage) => { receiveBatch([message]); }; + bridge.trxPluginRuntime.registerDecoder({ + id, + onMessage: (message: FtxMessage) => { receiveBatch([message]); }, + onBatch: receiveBatch, + restore: receiveBatch, + prune: () => { prune(); render(); }, + reset, + }); bridge.registerFt8FamilyBarRenderer?.(id, barFrames); const updatePeriod = (): void => { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat.ts index fd09030e..5abe1c1c 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat.ts @@ -3,17 +3,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later import type { LrptProgress, SatelliteImage, SatelliteLiveUpdate, SatellitePass, SatellitePassResponse } from "./satellite-types"; +import type { PluginRuntimeWindow } from "./runtime-contract.js"; type SatelliteView = "live" | "history" | "predictions"; interface SatelliteBridge { trxScheduleUiFrameJob?: (key: string, job: () => void) => void; clearSatPredictionDom?: () => void; updateSatLiveState?: (update: SatelliteLiveUpdate) => void; - onServerLrptProgress?: (message: LrptProgress) => void; - onServerLrptImage?: (message: SatelliteImage) => void; addSatMapOverlay?: (image: SatelliteImage) => void; - resetSatHistoryView?: () => void; - pruneSatHistoryView?: () => void; clearSatMapOverlays?: () => void; takeSchedulerControlForDecoderDisable?: (button: HTMLElement) => Promise; postPath?: (path: string) => Promise; @@ -22,7 +19,7 @@ interface SatelliteBridge { enableMapSourceFilter?: (source: string) => void; navigateToAprsMap?: (lat: number, lon: number) => void; } -const satWindow = window as unknown as SatelliteBridge; +const satWindow = window as unknown as SatelliteBridge & PluginRuntimeWindow; // --- SAT Plugin --- // Live view: decoder state, latest image card @@ -265,32 +262,43 @@ function addSatImage(img: SatelliteImage, decoder: "lrpt"): void { } // ── Server callbacks ──────────────────────────────────────────────── -satWindow.onServerLrptProgress = function (msg: LrptProgress) { +function onServerLrptProgress(msg: LrptProgress): void { if (satDom.status && (msg.mcu_count ?? 0) > 0) { satDom.status.textContent = `Receiving \u2014 ${String(msg.mcu_count ?? 0)} MCU rows decoded`; } -}; +} -satWindow.onServerLrptImage = function (msg: SatelliteImage) { +function onServerLrptImage(msg: SatelliteImage): void { if (satDom.status) satDom.status.textContent = "Image received (Meteor LRPT)"; addSatImage(msg, "lrpt"); if (msg.geo_bounds && msg.path && satWindow.addSatMapOverlay) { satWindow.addSatMapOverlay(msg); } -}; +} -satWindow.resetSatHistoryView = function () { +function resetSatHistoryView(): void { satImageHistory = []; if (satDom.historyList) satDom.historyList.innerHTML = ""; renderSatLatestCard(); renderSatHistoryTable(); satWindow.clearSatMapOverlays?.(); -}; +} -satWindow.pruneSatHistoryView = function () { +function pruneSatHistoryView(): void { renderSatHistoryTable(); renderSatLatestCard(); -}; +} + +satWindow.trxPluginRuntime.registerDecoder({ + id: "lrpt_image", + onMessage: onServerLrptImage, + reset: resetSatHistoryView, + prune: pruneSatHistoryView, +}); +satWindow.trxPluginRuntime.registerDecoder({ + id: "lrpt_progress", + onMessage: onServerLrptProgress, +}); // ── Toggle buttons ────────────────────────────────────────────────── const lrptDecodeToggleBtn = document.getElementById("lrpt-decode-toggle-btn"); @@ -320,7 +328,7 @@ document if (!await satWindow.trxUi.confirm({ title: "Clear satellite history?", message: "All stored satellite decodes will be permanently removed.", confirmLabel: "Clear history" })) return; try { await satWindow.postPath?.("/clear_lrpt_decode"); - satWindow.resetSatHistoryView?.(); + resetSatHistoryView(); } catch (e) { console.error("Weather satellite history clear failed", e); } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wefax.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wefax.ts index 4bf51969..d9b84fec 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wefax.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wefax.ts @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: GPL-2.0-or-later +import type { PluginRuntimeWindow } from "./runtime-contract.js"; + export {}; interface WefaxImage { @@ -29,15 +31,10 @@ interface WefaxBridge { trxScheduleUiFrameJob?: (key: string, job: () => void) => void; postPath?: (path: string) => Promise; takeSchedulerControlForDecoderDisable?: (button: HTMLElement) => Promise; - onServerWefaxProgress?: (message: WefaxProgress) => void; - onServerWefax?: (message: WefaxImage) => void; - restoreWefaxHistory?: (messages: WefaxImage[]) => void; - pruneWefaxHistoryView?: () => void; - resetWefaxHistoryView?: () => void; syncWefaxToggle?: (enabled: boolean) => void; } -const wefaxWindow = window as unknown as WefaxBridge; +const wefaxWindow = window as unknown as WefaxBridge & PluginRuntimeWindow; // --------------------------------------------------------------------------- // wefax.js — WEFAX decoder plugin for trx-frontend-http @@ -299,7 +296,7 @@ function addWefaxImage(msg: WefaxImage): void { } // ── SSE event handlers (public API) ───────────────────────────────── -wefaxWindow.onServerWefaxProgress = function (msg: WefaxProgress) { +function onServerWefaxProgress(msg: WefaxProgress): void { // State-only update (no image data): show decoder state in status. if (msg.state && !msg.line_data) { if (wefaxDom.status) { @@ -329,9 +326,9 @@ wefaxWindow.onServerWefaxProgress = function (msg: WefaxProgress) { wefaxDom.status.textContent = `Receiving \u2014 line ${String(msg.line_count ?? 0)}`; wefaxDom.status.style.color = 'var(--text-accent)'; } -}; +} -wefaxWindow.onServerWefax = function (msg: WefaxImage) { +function onServerWefax(msg: WefaxImage): void { addWefaxImage(msg); if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = 'none'; @@ -339,9 +336,9 @@ wefaxWindow.onServerWefax = function (msg: WefaxImage) { wefaxDom.status.textContent = `Complete \u2014 ${String(msg.line_count ?? 0)} lines`; wefaxDom.status.style.color = ''; } -}; +} -wefaxWindow.restoreWefaxHistory = function (messages: WefaxImage[]) { +function restoreWefaxHistory(messages: WefaxImage[]): void { if (!messages.length) return; for (const message of messages) { const tsMs = Number.isFinite(message.ts_ms) ? Number(message.ts_ms) : Date.now(); @@ -358,15 +355,15 @@ wefaxWindow.restoreWefaxHistory = function (messages: WefaxImage[]) { if (wefaxActiveView === 'history') { scheduleWefaxUi('wefax-history', renderWefaxHistoryTable); } -}; +} -wefaxWindow.pruneWefaxHistoryView = function () { +function pruneWefaxHistoryView(): void { pruneWefaxHistory(); renderWefaxHistoryTable(); renderWefaxLatestCard(); -}; +} -wefaxWindow.resetWefaxHistoryView = function () { +function resetWefaxHistoryView(): void { wefaxImageHistory = []; if (wefaxDom.historyList) wefaxDom.historyList.innerHTML = ''; if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = 'none'; @@ -378,7 +375,7 @@ wefaxWindow.resetWefaxHistoryView = function () { wefaxDom.status.textContent = 'Idle'; wefaxDom.status.style.color = ''; } -}; +} // ── Filter / sort handlers ────────────────────────────────────────── if (wefaxDom.filterInput) { @@ -424,7 +421,7 @@ if (wefaxDom.clearBtn) { wefaxDom.clearBtn.addEventListener('click', () => { void (async () => { try { await wefaxWindow.postPath?.('/clear_wefax_decode'); - wefaxWindow.resetWefaxHistoryView?.(); + resetWefaxHistoryView(); } catch (e) { console.error('WEFAX clear failed', e); } @@ -433,3 +430,15 @@ if (wefaxDom.clearBtn) { // ── Initial render ────────────────────────────────────────────────── renderWefaxLatestCard(); + +wefaxWindow.trxPluginRuntime.registerDecoder({ + id: "wefax", + onMessage: onServerWefax, + restore: restoreWefaxHistory, + prune: pruneWefaxHistoryView, + reset: resetWefaxHistoryView, +}); +wefaxWindow.trxPluginRuntime.registerDecoder({ + id: "wefax_progress", + onMessage: onServerWefaxProgress, +}); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wspr.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wspr.ts index 492e2777..728fea4b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wspr.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/wspr.ts @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: GPL-2.0-or-later +import type { PluginRuntimeWindow } from "./runtime-contract.js"; + export {}; interface WsprMessage { @@ -25,14 +27,9 @@ interface WsprBridge { takeSchedulerControlForDecoderDisable?: (button: HTMLElement) => Promise; postPath?: (path: string) => Promise; trxUi: { confirm(options: { title: string; message: string; confirmLabel: string }): Promise }; - onServerWsprBatch?: (messages: WsprMessage[]) => void; - restoreWsprHistory?: (messages: WsprMessage[]) => void; - pruneWsprHistoryView?: () => void; - resetWsprHistoryView?: () => void; - onServerWspr?: (message: WsprMessage) => void; [key: string]: unknown; } -const wsprWindow = window as unknown as WsprBridge; +const wsprWindow = window as unknown as WsprBridge & PluginRuntimeWindow; // --- WSPR Decoder Plugin (server-side decode) --- const wsprStatus = document.getElementById("wspr-status"); @@ -142,7 +139,7 @@ function normalizeServerWsprMessage(msg: WsprMessage): { raw: string; grids: str }; } -wsprWindow.onServerWsprBatch = function(messages: WsprMessage[]) { +function onServerWsprBatch(messages: WsprMessage[]): void { if (!Array.isArray(messages) || messages.length === 0) return; if (wsprStatus) wsprStatus.textContent = "Receiving"; const normalized: WsprMessage[] = []; @@ -161,17 +158,12 @@ wsprWindow.onServerWsprBatch = function(messages: WsprMessage[]) { wsprMessageHistory = normalized.concat(wsprMessageHistory); pruneWsprMessageHistory(); scheduleWsprHistoryRender(); -}; +} -wsprWindow.restoreWsprHistory = function(messages: WsprMessage[]) { - const callback = wsprWindow.onServerWsprBatch; - if (typeof callback === "function") callback(messages); -}; - -wsprWindow.pruneWsprHistoryView = function() { +function pruneWsprHistoryView(): void { pruneWsprMessageHistory(); renderWsprHistory(); -}; +} function escapeWsprHtml(input: string): string { return input @@ -266,12 +258,12 @@ function applyWsprFilterToRow(row: HTMLElement): void { row.style.display = message.includes(wsprFilterText) ? "" : "none"; } -wsprWindow.resetWsprHistoryView = function() { +function resetWsprHistoryView(): void { if (wsprMessagesEl) wsprMessagesEl.innerHTML = ""; wsprMessageHistory = []; renderWsprHistory(); if (wsprWindow.clearMapMarkersByType) wsprWindow.clearMapMarkersByType("wspr"); -}; +} if (wsprFilterInput) { wsprFilterInput.addEventListener("input", () => { @@ -311,14 +303,14 @@ document.getElementById("settings-clear-wspr-history")?.addEventListener("click" if (!await wsprWindow.trxUi.confirm({ title: "Clear WSPR history?", message: "All stored WSPR decodes will be permanently removed.", confirmLabel: "Clear history" })) return; try { await wsprWindow.postPath?.("/clear_wspr_decode"); - wsprWindow.resetWsprHistoryView?.(); + resetWsprHistoryView(); } catch (error: unknown) { console.error("WSPR history clear failed", error); } })(); }); -wsprWindow.onServerWspr = function(msg: WsprMessage) { +function onServerWspr(msg: WsprMessage): void { if (wsprStatus) wsprStatus.textContent = "Receiving"; const next = normalizeServerWsprMessage(msg); if (next.grids.length > 0 && wsprWindow.mapAddLocator) { @@ -328,4 +320,13 @@ wsprWindow.onServerWspr = function(msg: WsprMessage) { }); } addWsprMessage(next.history); -}; +} + +wsprWindow.trxPluginRuntime.registerDecoder({ + id: "wspr", + onMessage: onServerWspr, + onBatch: onServerWsprBatch, + restore: onServerWsprBatch, + prune: pruneWsprHistoryView, + reset: resetWsprHistoryView, +}); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/cw.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/cw.test.mjs index bf19e89d..6c65fe24 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/cw.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/cw.test.mjs @@ -37,13 +37,15 @@ test("CW entry appends server-decoded text and registers lifecycle callbacks", a Math, console, }); + const runtime = await readFile(new URL("../../assets/web/generated/plugin-runtime.js", import.meta.url), "utf8"); const source = await readFile(new URL("../../assets/web/generated/cw.js", import.meta.url), "utf8"); + new vm.Script(runtime).runInContext(context); new vm.Script(source).runInContext(context); - window.onServerCw({ text: "CQ", wpm: 18, tone_hz: 700, signal_on: true }); + window.trxPluginRuntime.dispatch("cw", { text: "CQ", wpm: 18, tone_hz: 700, signal_on: true }); assert.equal(status.textContent, "Receiving"); assert.equal(output.children.length, 1); assert.equal(output.children[0].textContent, "CQ"); - assert.equal(typeof window.resetCwHistoryView, "function"); - assert.equal(typeof window.restoreCwHistory, "function"); + assert.equal(window.onServerCw, undefined); + assert.equal(window.trxPluginRuntime.hasDecoder("cw"), true); }); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/ftx-family.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/ftx-family.test.mjs index 3734ee9f..548a8e41 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/ftx-family.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/ftx-family.test.mjs @@ -29,10 +29,12 @@ test("FT2 entry normalizes audio offsets and registers typed callbacks", async ( Array, console, }); + const runtime = await readFile(new URL("../../assets/web/generated/plugin-runtime.js", import.meta.url), "utf8"); const source = await readFile(new URL("../../assets/web/generated/ft2.js", import.meta.url), "utf8"); + new vm.Script(runtime).runInContext(context); new vm.Script(source).runInContext(context); - window.onServerFt2({ message: "CQ SP0ABC JO91", freq_hz: 1_250, ts_ms: Date.now(), snr_db: -8 }); + window.trxPluginRuntime.dispatch("ft2", { message: "CQ SP0ABC JO91", freq_hz: 1_250, ts_ms: Date.now(), snr_db: -8 }); assert.equal(mapMessages.length, 1); assert.equal(mapMessages[0][4].freq_hz, 14_075_250); const bar = barRenderer(); @@ -59,12 +61,14 @@ test("FT8 entry installs shared parsing without relying on script globals", asyn Set, console, }); + const runtime = await readFile(new URL("../../assets/web/generated/plugin-runtime.js", import.meta.url), "utf8"); const source = await readFile(new URL("../../assets/web/generated/ft8.js", import.meta.url), "utf8"); + new vm.Script(runtime).runInContext(context); new vm.Script(source).runInContext(context); const details = window.ft8ExtractLocatorDetails("K1ABC SP0ABC JO91"); assert.equal(details[0].source, "SP0ABC"); assert.equal(details[0].target, "K1ABC"); - window.onServerFt8({ message: "K1ABC SP0ABC JO91", freq_hz: 500, ts_ms: Date.now() }); + window.trxPluginRuntime.dispatch("ft8", { message: "K1ABC SP0ABC JO91", freq_hz: 500, ts_ms: Date.now() }); assert.equal(forwarded[0][4].freq_hz, 7_074_500); }); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat.test.mjs index 6c2de006..2f8b4ee6 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat.test.mjs @@ -26,12 +26,14 @@ test("satellite entry registers lifecycle callbacks and forwards georeferenced i Error, console, }); + const runtime = await readFile(new URL("../../assets/web/generated/plugin-runtime.js", import.meta.url), "utf8"); const source = await readFile(new URL("../../assets/web/generated/sat.js", import.meta.url), "utf8"); + new vm.Script(runtime).runInContext(context); new vm.Script(source).runInContext(context); - window.onServerLrptImage({ path: "/images/lrpt.png", geo_bounds: [50, 10, 55, 20], mcu_count: 100 }); + window.trxPluginRuntime.dispatch("lrpt_image", { path: "/images/lrpt.png", geo_bounds: [50, 10, 55, 20], mcu_count: 100 }); assert.equal(overlays.length, 1); assert.deepEqual(overlays[0].geo_bounds, [50, 10, 55, 20]); assert.equal(typeof window.updateSatLiveState, "function"); - assert.equal(typeof window.resetSatHistoryView, "function"); + assert.equal(window.onServerLrptImage, undefined); }); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wefax.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wefax.test.mjs index 08c41edf..4e413e17 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wefax.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wefax.test.mjs @@ -19,12 +19,14 @@ test("WEFAX entry exposes typed lifecycle handlers and renders decoder state", a Uint8Array, console, }); + const runtime = await readFile(new URL("../../assets/web/generated/plugin-runtime.js", import.meta.url), "utf8"); const source = await readFile(new URL("../../assets/web/generated/wefax.js", import.meta.url), "utf8"); + new vm.Script(runtime).runInContext(context); new vm.Script(source).runInContext(context); - assert.equal(typeof window.onServerWefaxProgress, "function"); - assert.equal(typeof window.restoreWefaxHistory, "function"); - window.onServerWefaxProgress({ state: "Scanning 12 MHz" }); + assert.equal(window.onServerWefaxProgress, undefined); + assert.equal(window.trxPluginRuntime.hasDecoder("wefax_progress"), true); + window.trxPluginRuntime.dispatch("wefax_progress", { state: "Scanning 12 MHz" }); assert.equal(status.textContent, "Scanning 12 MHz"); assert.equal(status.style.color, "var(--text-accent)"); }); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wspr.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wspr.test.mjs index 9e341ef4..540f8ae7 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wspr.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/wspr.test.mjs @@ -25,10 +25,12 @@ test("WSPR entry forwards decoded locators at their absolute frequency", async ( Element: class Element {}, console, }); + const runtime = await readFile(new URL("../../assets/web/generated/plugin-runtime.js", import.meta.url), "utf8"); const source = await readFile(new URL("../../assets/web/generated/wspr.js", import.meta.url), "utf8"); + new vm.Script(runtime).runInContext(context); new vm.Script(source).runInContext(context); - window.onServerWspr({ message: "SP0ABC JO91 37", freq_hz: 1_420, ts_ms: Date.now() }); + window.trxPluginRuntime.dispatch("wspr", { message: "SP0ABC JO91 37", freq_hz: 1_420, ts_ms: Date.now() }); assert.equal(forwarded.length, 1); assert.equal(forwarded[0][1][0], "JO91"); assert.equal(forwarded[0][4].freq_hz, 14_097_020);