From 26a167cb820d88e3b812493fb7f0f4dbedb8b173 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sat, 1 Aug 2026 16:05:40 +0200 Subject: [PATCH] refactor: replace feature globals with typed services --- .../assets/web/generated/app.js | 90 +++++++++--------- .../assets/web/generated/background-decode.js | 10 +- .../assets/web/generated/bookmarks.js | 13 +-- .../assets/web/generated/map-core.js | 2 + .../assets/web/generated/scheduler.js | 4 +- .../assets/web/generated/vchan.js | 30 ++++-- .../trx-frontend-http/frontend/src/app.js | 92 ++++++++++--------- .../frontend/src/map-core.ts | 2 + .../frontend/src/plugins/background-decode.ts | 19 ++-- .../frontend/src/plugins/bookmarks.ts | 27 +++--- .../frontend/src/plugins/scheduler-types.ts | 8 +- .../frontend/src/plugins/scheduler.ts | 4 +- .../frontend/src/plugins/vchan.ts | 49 +++++++--- .../frontend/tests/background-decode.test.mjs | 3 +- .../frontend/tests/bookmarks.test.mjs | 2 + .../frontend/tests/vchan.test.mjs | 13 ++- 16 files changed, 219 insertions(+), 149 deletions(-) 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 2dc1ab25..49ace0b9 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 @@ -384,7 +384,7 @@ sdrSquelchSupported = false; } updateSdrSquelchControlVisibility(); - if (typeof vchanApplyCapabilities === "function") vchanApplyCapabilities(caps); + window.trx?.modules?.vchan?.applyCapabilities(caps); } var freqEl = document.getElementById("freq"); var centerFreqEl = document.getElementById("center-freq"); @@ -1217,9 +1217,9 @@ if (themeToggleBtn) { themeToggleBtn.addEventListener("click", () => { setTheme(currentTheme() === "dark" ? "light" : "dark"); - updateMapBaseLayerForTheme(currentTheme()); - syncLocatorMarkerStyles(); - refreshAisMarkerColors(); + window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme()); + window.trx.modules.map?.syncLocatorMarkerStyles(); + window.trx.modules.map?.refreshAisMarkerColors(); scheduleOverviewDraw(); if (typeof scheduleSpectrumDraw === "function" && lastSpectrumData) scheduleSpectrumDraw(); }); @@ -1227,9 +1227,9 @@ if (headerStylePickSelect) { headerStylePickSelect.addEventListener("change", () => { setStyle(headerStylePickSelect.value); - updateMapBaseLayerForTheme(currentTheme()); - syncLocatorMarkerStyles(); - refreshAisMarkerColors(); + window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme()); + window.trx.modules.map?.syncLocatorMarkerStyles(); + window.trx.modules.map?.refreshAisMarkerColors(); }); } function readyText() { @@ -1315,9 +1315,9 @@ window.trxUi?.setActiveRig(lastActiveRigId); if (rigListChanged) { window.trx.modules.scheduler?.setRig(lastActiveRigId); - if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId); - if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker(); - if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); + window.trx.modules.backgroundDecode?.setRig(lastActiveRigId); + window.trx.modules.bookmarks?.populateScopePicker(); + window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || ""); } window.trx.modules.map?.updateMapRigFilter(); } @@ -1562,12 +1562,13 @@ } } } - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels)) { - vchanChannels.forEach((ch) => { + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { + virtualChannels.forEach((ch) => { if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return; const xc = hzToX(ch.freq_hz); if (xc < 0 || xc > W) return; - const isActive = ch.id === vchanActiveId; + const isActive = ch.id === window.trx.modules.vchan?.activeId; const color = cssColorToRgba("#38bdf8"); if (isActive) { signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5)); @@ -1852,7 +1853,8 @@ refreshWavelengthDisplay(lastFreqHz); } function activeRdsChannelId() { - if (typeof vchanActiveId !== "undefined" && vchanActiveId) return vchanActiveId; + const virtualChannelId = window.trx.modules.vchan?.activeId; + if (virtualChannelId) return virtualChannelId; return null; } function activeChannelRds() { @@ -1861,23 +1863,25 @@ if (activeId) { const rds = vchanRdsById.get(activeId); if (rds) return rds; - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) { - if (vchanChannels[0].id === activeId) return primaryRds; + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { + if (virtualChannels[0].id === activeId) return primaryRds; } } return primaryRds; } function activeChannelIsWfm() { - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) { + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { const activeId = activeRdsChannelId(); - const active = vchanChannels.find((ch) => ch.id === activeId) || vchanChannels[0]; + const active = virtualChannels.find((ch) => ch.id === activeId) || virtualChannels[0]; return String(active?.mode || "").toUpperCase() === "WFM"; } return lastModeName === "WFM"; } function activeChannelFreqHz() { - if (typeof vchanActiveChannel === "function") { - const ch = vchanActiveChannel(); + if (window.trx.modules.vchan) { + const ch = window.trx.modules.vchan.activeChannel(); if (Number.isFinite(ch?.freq_hz)) return ch.freq_hz; } return lastFreqHz; @@ -1899,11 +1903,12 @@ } function collectRdsOverlayEntries() { const entries = []; - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) { - for (const ch of vchanChannels) { + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { + for (const ch of virtualChannels) { if (String(ch?.mode || "").toUpperCase() !== "WFM") continue; if (!Number.isFinite(ch?.freq_hz)) continue; - const rds = vchanRdsById.get(ch.id) || (vchanChannels[0].id === ch.id ? primaryRds : null); + const rds = vchanRdsById.get(ch.id) || (virtualChannels[0].id === ch.id ? primaryRds : null); if (!rds) continue; entries.push({ id: ch.id, freq_hz: ch.freq_hz, rds }); } @@ -3145,7 +3150,7 @@ ${unsupportedBandSummary()}`; prevRenderData.mode = update.status.mode; const mode = normalizeMode(update.status.mode); const modeUpper2 = mode ? mode.toUpperCase() : ""; - const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual(); + const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true; if (!onVirtual) { modeEl.value = modeUpper2; if (modeUpper2 === "WFM" && lastModeName !== "WFM") { @@ -3500,10 +3505,10 @@ ${unsupportedBandSummary()}`; sseSessionId = d.session_id || null; } catch (_) { } - if (typeof vchanHandleSession === "function") vchanHandleSession(evt.data); + window.trx.modules.vchan?.handleSession(evt.data); }); es.addEventListener("channels", (evt) => { - if (typeof vchanHandleChannels === "function") vchanHandleChannels(evt.data); + window.trx.modules.vchan?.handleChannels(evt.data); }); es.onerror = () => { if (es.readyState === EventSource.CLOSED) { @@ -3600,9 +3605,7 @@ ${unsupportedBandSummary()}`; async function takeSchedulerControlForDecoderDisable(buttonEl) { const enabled = buttonEl?.dataset?.enabled === "true" || /^\s*Disable\b/i.test(buttonEl?.textContent || ""); if (!enabled) return; - if (typeof window.vchanTakeSchedulerControl === "function") { - await window.vchanTakeSchedulerControl(); - } + await window.trx.modules.vchan?.takeSchedulerControl(); } window.takeSchedulerControlForDecoderDisable = takeSchedulerControlForDecoderDisable; async function switchRigFromSelect(selectEl) { @@ -3635,8 +3638,8 @@ ${unsupportedBandSummary()}`; updateRigIdentitySummary(lastActiveRigId); window.trxUi?.setActiveRig(lastActiveRigId); window.trx.modules.scheduler?.setRig(lastActiveRigId); - if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId); - if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); + window.trx.modules.backgroundDecode?.setRig(lastActiveRigId); + window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || ""); window.trx.modules.map?.syncAprsReceiverMarker(); connect(); stopSpectrumStreaming(); @@ -3914,7 +3917,7 @@ ${unsupportedBandSummary()}`; setControlPending(modeEl, true); showHint("Setting mode…"); try { - if (typeof vchanInterceptMode === "function" && await vchanInterceptMode(mode)) { + if (await window.trx.modules.vchan?.interceptMode(mode)) { showHint("Channel mode set", 1500); return; } @@ -4047,7 +4050,7 @@ ${unsupportedBandSummary()}`; scheduleSpectrumDraw(); } try { - if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(clamped)) return; + if (await window.trx.modules.vchan?.interceptBandwidth(clamped)) return; await postPath(`/set_bandwidth?hz=${clamped}`); if (Number.isFinite(lastFreqHz)) { await ensureTunedBandwidthCoverage(lastFreqHz); @@ -4131,7 +4134,7 @@ ${unsupportedBandSummary()}`; } async function applyAutoBandwidth() { if (!lastSpectrumData || lastFreqHz == null) return; - const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual(); + const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true; const interference = onVirtual ? {} : { cci: lastWfmCci, aci: lastWfmAci }; const estimated = estimateOccupiedBandwidth(lastSpectrumData, lastFreqHz, interference); if (!Number.isFinite(estimated) || estimated <= 0) { @@ -4155,7 +4158,7 @@ ${unsupportedBandSummary()}`; } window.trxUi?.notify(`Auto BW: ${formatBwLabel(estimated)} — ${reason}`, { kind: "success", duration: 5e3 }); try { - if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(estimated)) return; + if (await window.trx.modules.vchan?.interceptBandwidth(estimated)) return; await postPath(`/set_bandwidth?hz=${estimated}`); if (Number.isFinite(lastFreqHz)) { await ensureTunedBandwidthCoverage(lastFreqHz); @@ -4373,9 +4376,9 @@ ${unsupportedBandSummary()}`; function initSettingsUI() { window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole); window.trx.modules.scheduler?.wireEvents(); - if (typeof initBackgroundDecode === "function") { - initBackgroundDecode(lastActiveRigId, authRole); - wireBackgroundDecodeEvents(); + if (window.trx.modules.backgroundDecode) { + window.trx.modules.backgroundDecode.initialize(lastActiveRigId, authRole); + window.trx.modules.backgroundDecode.wireEvents(); } } document.getElementById("auth-form").addEventListener("submit", async (e) => { @@ -4619,7 +4622,7 @@ ${unsupportedBandSummary()}`; if (!bm) return null; const parts = []; if (bm.name) parts.push(String(bm.name)); - if (typeof bmFmtFreq === "function") parts.push(bmFmtFreq(bm.freq_hz)); + if (window.trx.modules.bookmarks) parts.push(window.trx.modules.bookmarks.formatFrequency(bm.freq_hz)); if (bm.mode) parts.push(String(bm.mode)); if (bm.locator) parts.push(String(bm.locator)); const distance = bookmarkDistanceText(bm); @@ -6517,8 +6520,9 @@ ${unsupportedBandSummary()}`; } vchanRdsById = next; vchanSignalDbById = nextSig; - if (typeof vchanActiveId !== "undefined" && vchanActiveId && nextSig.has(vchanActiveId)) { - sigLastDbm = nextSig.get(vchanActiveId); + const virtualChannelId = window.trx.modules.vchan?.activeId; + if (virtualChannelId && nextSig.has(virtualChannelId)) { + sigLastDbm = nextSig.get(virtualChannelId); refreshSigStrengthDisplay(); } updateRdsPsOverlay(primaryRds); @@ -7159,7 +7163,7 @@ ${unsupportedBandSummary()}`; } function createBookmarkChip(bm, colorMap, options = {}) { const span = document.createElement("span"); - const freqStr = typeof bmFmtFreq === "function" ? bmFmtFreq(bm.freq_hz) : bm.freq_hz + " Hz"; + const freqStr = window.trx.modules.bookmarks ? window.trx.modules.bookmarks.formatFrequency(bm.freq_hz) : bm.freq_hz + " Hz"; const esc = (s) => String(s).replace(/&/g, "&").replace(//g, ">"); span.className = "spectrum-bookmark-chip"; if (options.sideStack) { @@ -7745,7 +7749,7 @@ ${unsupportedBandSummary()}`; if (_bwDragEdge) { try { const bwHz = Math.round(currentBandwidthHz); - if (!(typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(bwHz))) { + if (!await window.trx.modules.vchan?.interceptBandwidth(bwHz)) { await postPath(`/set_bandwidth?hz=${bwHz}`); if (Number.isFinite(lastFreqHz)) { await ensureTunedBandwidthCoverage(lastFreqHz, currentBandwidthHz); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js index 289fad86..88d9afec 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js @@ -356,7 +356,11 @@ var bgdWindow = window; }); } } - bgdWindow.initBackgroundDecode = initBackgroundDecode; - bgdWindow.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents; - bgdWindow.setBackgroundDecodeRig = setBackgroundDecodeRig; + bgdWindow.trx ??= {}; + bgdWindow.trx.modules ??= {}; + bgdWindow.trx.modules.backgroundDecode = { + initialize: initBackgroundDecode, + wireEvents: wireBackgroundDecodeEvents, + setRig: setBackgroundDecodeRig + }; })(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/bookmarks.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/bookmarks.js index 6bcd316f..b7bfd423 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/bookmarks.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/bookmarks.js @@ -343,15 +343,13 @@ function bmApply(bm) { bridge.scheduleSpectrumDraw(); } const tunePromise = (async () => { - if (typeof bridge.vchanTakeSchedulerControl === "function") { - await bridge.vchanTakeSchedulerControl(); - } - const onVirtual = typeof bridge.vchanInterceptMode === "function" && await bridge.vchanInterceptMode(bm.mode); + await bridge.trx?.modules?.vchan?.takeSchedulerControl(); + const onVirtual = await bridge.trx?.modules?.vchan?.interceptMode(bm.mode) ?? false; if (!onVirtual) { await bridge.postPath("/set_mode?mode=" + encodeURIComponent(bm.mode)); } if (bm.bandwidth_hz) { - const bwHandledByVchan = typeof bridge.vchanInterceptBandwidth === "function" && await bridge.vchanInterceptBandwidth(bm.bandwidth_hz); + const bwHandledByVchan = await bridge.trx?.modules?.vchan?.interceptBandwidth(bm.bandwidth_hz) ?? false; if (!bwHandledByVchan) { await bridge.postPath(`/set_bandwidth?hz=${bm.bandwidth_hz}`); } @@ -414,7 +412,10 @@ bridge.trx.modules.bookmarks = { invalidateColors() { bmOverlayRevision += 1; }, - apply: bmApply + apply: bmApply, + formatFrequency: bmFmtFreq, + fetch: bmFetch, + populateScopePicker: bmPopulateScopePicker }; function bmUpdateSelectionUi() { const count = bmSelected.size; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/map-core.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/map-core.js index 0f033c1f..5cff0e48 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/map-core.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/map-core.js @@ -3090,6 +3090,8 @@ var mapWindow = window; createAisMarker, getAisAccentColor, refreshAisMarkerColors, + updateMapBaseLayerForTheme, + syncLocatorMarkerStyles, setMapRadioPathTo, buildReceiverPopupHtml, rebuildDecodeContactPaths, diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/scheduler.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/scheduler.js index be748f6c..d1a7b1f4 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/scheduler.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/scheduler.js @@ -865,12 +865,12 @@ function schedulerEl(id) { const targetId = target.id; schedulerStepPending = true; renderSchedulerStepControls(); - Promise.resolve(schedulerWindow.vchanTakeSchedulerControl?.() ?? null).then(function() { + Promise.resolve(schedulerWindow.trx.modules.vchan?.takeSchedulerControl() ?? null).then(function() { return apiActivateSchedulerEntry(rigId, targetId); }).then(function(status) { currentSchedulerStatus = status || null; return Promise.resolve( - schedulerWindow.vchanToggleSchedulerRelease?.() ?? null + schedulerWindow.trx.modules.vchan?.releaseToScheduler() ?? null ).then(function() { renderStatus(status); renderSchedulerInterleaveStatus(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/vchan.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/vchan.js index 07b4edca..a3d42565 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/vchan.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/vchan.js @@ -93,7 +93,6 @@ async function vchanTakeSchedulerControl() { console.error("scheduler control takeover failed", e); } } -vchanWindow.vchanTakeSchedulerControl = vchanTakeSchedulerControl; function vchanHandleSession(data) { try { const d = JSON.parse(data); @@ -123,8 +122,6 @@ function vchanHandleChannels(data) { console.warn("vchan: bad channels event", e); } } -vchanWindow.vchanHandleSession = vchanHandleSession; -vchanWindow.vchanHandleChannels = vchanHandleChannels; function vchanRender() { const picker = document.getElementById("vchan-picker"); if (!picker) return; @@ -267,12 +264,10 @@ function vchanApplyCapabilities(caps) { picker.style.display = caps && caps.filter_controls ? "" : "none"; vchanRenderSchedulerRelease(); } -vchanWindow.vchanApplyCapabilities = vchanApplyCapabilities; function vchanIsOnVirtual() { if (!vchanActiveId || vchanChannels.length === 0) return false; return vchanActiveId !== vchanChannels[0]?.id; } -vchanWindow.vchanIsOnVirtual = vchanIsOnVirtual; function vchanActiveChannel() { return vchanChannels.find((c) => c.id === vchanActiveId) || null; } @@ -406,15 +401,34 @@ async function vchanSetChannelMode(mode) { console.error("vchan: set mode error", e); } } -vchanWindow.vchanInterceptMode = async function(mode) { +async function vchanInterceptMode(mode) { if (!vchanIsOnVirtual()) return false; await vchanSetChannelMode(mode); return true; -}; -vchanWindow.vchanInterceptBandwidth = async function(bwHz) { +} +async function vchanInterceptBandwidth(bwHz) { if (!vchanIsOnVirtual()) return false; await vchanSetChannelBandwidth(bwHz); return true; +} +vchanWindow.trx ??= {}; +vchanWindow.trx.modules ??= {}; +vchanWindow.trx.modules.vchan = { + get channels() { + return vchanChannels; + }, + get activeId() { + return vchanActiveId; + }, + activeChannel: vchanActiveChannel, + applyCapabilities: vchanApplyCapabilities, + handleSession: vchanHandleSession, + handleChannels: vchanHandleChannels, + isOnVirtual: vchanIsOnVirtual, + interceptMode: vchanInterceptMode, + interceptBandwidth: vchanInterceptBandwidth, + takeSchedulerControl: vchanTakeSchedulerControl, + releaseToScheduler: vchanToggleSchedulerRelease }; (function() { const original = vchanWindow.setRigFrequency; 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 eff494aa..7387afcc 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 @@ -384,7 +384,7 @@ function applyCapabilities(caps) { sdrSquelchSupported = false; } updateSdrSquelchControlVisibility(); - if (typeof vchanApplyCapabilities === "function") vchanApplyCapabilities(caps); + window.trx?.modules?.vchan?.applyCapabilities(caps); } const freqEl = document.getElementById("freq"); @@ -1148,9 +1148,9 @@ setStyle(savedStyle); if (themeToggleBtn) { themeToggleBtn.addEventListener("click", () => { setTheme(currentTheme() === "dark" ? "light" : "dark"); - updateMapBaseLayerForTheme(currentTheme()); - syncLocatorMarkerStyles(); - refreshAisMarkerColors(); + window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme()); + window.trx.modules.map?.syncLocatorMarkerStyles(); + window.trx.modules.map?.refreshAisMarkerColors(); scheduleOverviewDraw(); if (typeof scheduleSpectrumDraw === "function" && lastSpectrumData) scheduleSpectrumDraw(); }); @@ -1159,9 +1159,9 @@ if (themeToggleBtn) { if (headerStylePickSelect) { headerStylePickSelect.addEventListener("change", () => { setStyle(headerStylePickSelect.value); - updateMapBaseLayerForTheme(currentTheme()); - syncLocatorMarkerStyles(); - refreshAisMarkerColors(); + window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme()); + window.trx.modules.map?.syncLocatorMarkerStyles(); + window.trx.modules.map?.refreshAisMarkerColors(); }); } @@ -1261,9 +1261,9 @@ function applyRigList(activeRigId, rigIds, displayNames) { window.trxUi?.setActiveRig(lastActiveRigId); if (rigListChanged) { window.trx.modules.scheduler?.setRig(lastActiveRigId); - if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId); - if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker(); - if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); + window.trx.modules.backgroundDecode?.setRig(lastActiveRigId); + window.trx.modules.bookmarks?.populateScopePicker(); + window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || ""); } window.trx.modules.map?.updateMapRigFilter(); } @@ -1534,12 +1534,13 @@ function drawSignalOverlay() { } // Virtual channel markers (sky-blue dashed lines, active one is solid). - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels)) { - vchanChannels.forEach(ch => { + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { + virtualChannels.forEach(ch => { if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return; const xc = hzToX(ch.freq_hz); if (xc < 0 || xc > W) return; - const isActive = ch.id === vchanActiveId; + const isActive = ch.id === window.trx.modules.vchan?.activeId; const color = cssColorToRgba("#38bdf8"); if (isActive) { signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5)); @@ -1858,7 +1859,8 @@ function refreshFreqDisplay() { } function activeRdsChannelId() { - if (typeof vchanActiveId !== "undefined" && vchanActiveId) return vchanActiveId; + const virtualChannelId = window.trx.modules.vchan?.activeId; + if (virtualChannelId) return virtualChannelId; return null; } @@ -1868,25 +1870,27 @@ function activeChannelRds() { if (activeId) { const rds = vchanRdsById.get(activeId); if (rds) return rds; - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) { - if (vchanChannels[0].id === activeId) return primaryRds; + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { + if (virtualChannels[0].id === activeId) return primaryRds; } } return primaryRds; } function activeChannelIsWfm() { - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) { + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { const activeId = activeRdsChannelId(); - const active = vchanChannels.find((ch) => ch.id === activeId) || vchanChannels[0]; + const active = virtualChannels.find((ch) => ch.id === activeId) || virtualChannels[0]; return String(active?.mode || "").toUpperCase() === "WFM"; } return lastModeName === "WFM"; } function activeChannelFreqHz() { - if (typeof vchanActiveChannel === "function") { - const ch = vchanActiveChannel(); + if (window.trx.modules.vchan) { + const ch = window.trx.modules.vchan.activeChannel(); if (Number.isFinite(ch?.freq_hz)) return ch.freq_hz; } return lastFreqHz; @@ -1923,11 +1927,12 @@ function buildRdsOverlayHtml(rds) { function collectRdsOverlayEntries() { const entries = []; - if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) { - for (const ch of vchanChannels) { + const virtualChannels = window.trx.modules.vchan?.channels || []; + if (virtualChannels.length > 0) { + for (const ch of virtualChannels) { if (String(ch?.mode || "").toUpperCase() !== "WFM") continue; if (!Number.isFinite(ch?.freq_hz)) continue; - const rds = vchanRdsById.get(ch.id) || (vchanChannels[0].id === ch.id ? primaryRds : null); + const rds = vchanRdsById.get(ch.id) || (virtualChannels[0].id === ch.id ? primaryRds : null); if (!rds) continue; entries.push({ id: ch.id, freq_hz: ch.freq_hz, rds }); } @@ -3378,7 +3383,7 @@ function render(update) { prevRenderData.mode = update.status.mode; const mode = normalizeMode(update.status.mode); const modeUpper = mode ? mode.toUpperCase() : ""; - const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual(); + const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true; // When subscribed to a virtual channel the mode picker must reflect // that channel's mode, not the primary rig mode. Skip the update here; // vchan.js will apply the correct mode via vchanSyncModeDisplay(). @@ -3766,10 +3771,10 @@ function connect() { const d = JSON.parse(evt.data); sseSessionId = d.session_id || null; } catch (_) {} - if (typeof vchanHandleSession === "function") vchanHandleSession(evt.data); + window.trx.modules.vchan?.handleSession(evt.data); }); es.addEventListener("channels", evt => { - if (typeof vchanHandleChannels === "function") vchanHandleChannels(evt.data); + window.trx.modules.vchan?.handleChannels(evt.data); }); es.onerror = () => { // Check if this is an auth error by looking at readyState @@ -3891,9 +3896,7 @@ async function takeSchedulerControlForDecoderDisable(buttonEl) { const enabled = buttonEl?.dataset?.enabled === "true" || /^\s*Disable\b/i.test(buttonEl?.textContent || ""); if (!enabled) return; - if (typeof window.vchanTakeSchedulerControl === "function") { - await window.vchanTakeSchedulerControl(); - } + await window.trx.modules.vchan?.takeSchedulerControl(); } window.takeSchedulerControlForDecoderDisable = takeSchedulerControlForDecoderDisable; @@ -3927,8 +3930,8 @@ async function switchRigFromSelect(selectEl) { updateRigIdentitySummary(lastActiveRigId); window.trxUi?.setActiveRig(lastActiveRigId); window.trx.modules.scheduler?.setRig(lastActiveRigId); - if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId); - if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); + window.trx.modules.backgroundDecode?.setRig(lastActiveRigId); + window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || ""); window.trx.modules.map?.syncAprsReceiverMarker(); connect(); stopSpectrumStreaming(); @@ -4234,7 +4237,7 @@ async function applyModeFromPicker() { setControlPending(modeEl, true); showHint("Setting mode…"); try { - if (typeof vchanInterceptMode === "function" && await vchanInterceptMode(mode)) { + if (await window.trx.modules.vchan?.interceptMode(mode)) { showHint("Channel mode set", 1500); return; } @@ -4382,7 +4385,7 @@ async function applyBandwidthFromInput() { scheduleSpectrumDraw(); } try { - if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(clamped)) return; + if (await window.trx.modules.vchan?.interceptBandwidth(clamped)) return; await postPath(`/set_bandwidth?hz=${clamped}`); if (Number.isFinite(lastFreqHz)) { await ensureTunedBandwidthCoverage(lastFreqHz); @@ -4493,7 +4496,7 @@ async function applyAutoBandwidth() { if (!lastSpectrumData || lastFreqHz == null) return; // WFM interference telemetry belongs to the primary DSP channel. Do not // apply it to a virtual channel, where it would describe the wrong signal. - const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual(); + const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true; const interference = onVirtual ? {} : { cci: lastWfmCci, aci: lastWfmAci }; const estimated = estimateOccupiedBandwidth(lastSpectrumData, lastFreqHz, interference); if (!Number.isFinite(estimated) || estimated <= 0) { @@ -4517,7 +4520,7 @@ async function applyAutoBandwidth() { } window.trxUi?.notify(`Auto BW: ${formatBwLabel(estimated)} — ${reason}`, { kind: "success", duration: 5000 }); try { - if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(estimated)) return; + if (await window.trx.modules.vchan?.interceptBandwidth(estimated)) return; await postPath(`/set_bandwidth?hz=${estimated}`); if (Number.isFinite(lastFreqHz)) { await ensureTunedBandwidthCoverage(lastFreqHz); @@ -4752,9 +4755,9 @@ async function initializeApp() { function initSettingsUI() { window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole); window.trx.modules.scheduler?.wireEvents(); - if (typeof initBackgroundDecode === "function") { - initBackgroundDecode(lastActiveRigId, authRole); - wireBackgroundDecodeEvents(); + if (window.trx.modules.backgroundDecode) { + window.trx.modules.backgroundDecode.initialize(lastActiveRigId, authRole); + window.trx.modules.backgroundDecode.wireEvents(); } } @@ -4915,7 +4918,7 @@ function buildBookmarkTooltipText(bm) { if (!bm) return null; const parts = []; if (bm.name) parts.push(String(bm.name)); - if (typeof bmFmtFreq === "function") parts.push(bmFmtFreq(bm.freq_hz)); + if (window.trx.modules.bookmarks) parts.push(window.trx.modules.bookmarks.formatFrequency(bm.freq_hz)); if (bm.mode) parts.push(String(bm.mode)); if (bm.locator) parts.push(String(bm.locator)); const distance = bookmarkDistanceText(bm); @@ -6941,8 +6944,9 @@ function startSpectrumStreaming() { } vchanRdsById = next; vchanSignalDbById = nextSig; - if (typeof vchanActiveId !== "undefined" && vchanActiveId && nextSig.has(vchanActiveId)) { - sigLastDbm = nextSig.get(vchanActiveId); + const virtualChannelId = window.trx.modules.vchan?.activeId; + if (virtualChannelId && nextSig.has(virtualChannelId)) { + sigLastDbm = nextSig.get(virtualChannelId); refreshSigStrengthDisplay(); } updateRdsPsOverlay(primaryRds); @@ -7691,8 +7695,8 @@ function bmCategoryColorMap() { function createBookmarkChip(bm, colorMap, options = {}) { const span = document.createElement("span"); - const freqStr = typeof bmFmtFreq === "function" - ? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz"; + const freqStr = window.trx.modules.bookmarks + ? window.trx.modules.bookmarks.formatFrequency(bm.freq_hz) : bm.freq_hz + "\u202fHz"; const esc = (s) => String(s) .replace(/&/g, "&").replace(//g, ">"); span.className = "spectrum-bookmark-chip"; @@ -8355,7 +8359,7 @@ if (spectrumCanvas || overviewCanvas) { if (_bwDragEdge) { try { const bwHz = Math.round(currentBandwidthHz); - if (!(typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(bwHz))) { + if (!(await window.trx.modules.vchan?.interceptBandwidth(bwHz))) { await postPath(`/set_bandwidth?hz=${bwHz}`); if (Number.isFinite(lastFreqHz)) { await ensureTunedBandwidthCoverage(lastFreqHz, currentBandwidthHz); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/map-core.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/map-core.ts index 7bb17e91..ae37d27d 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/map-core.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/map-core.ts @@ -3653,6 +3653,8 @@ const mapWindow = window as unknown as MapWindow; createAisMarker, getAisAccentColor, refreshAisMarkerColors, + updateMapBaseLayerForTheme, + syncLocatorMarkerStyles, setMapRadioPathTo, buildReceiverPopupHtml, rebuildDecodeContactPaths, diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts index 79a374eb..e2b55686 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts @@ -40,9 +40,12 @@ interface BackgroundBridge { decoderRegistry?: DecoderDescriptor[]; authEnabled?: boolean; trxUi: { confirm(options: { title: string; message: string; confirmLabel: string }): Promise }; - initBackgroundDecode?: (rigId: string | null, role: string | null) => void; - wireBackgroundDecodeEvents?: () => void; - setBackgroundDecodeRig?: (rigId: string | null) => void; + trx?: { modules?: { backgroundDecode?: BackgroundDecodeService } }; +} +interface BackgroundDecodeService { + initialize(rigId: string | null, role: string | null): void; + wireEvents(): void; + setRig(rigId: string | null): void; } interface WiredElement extends HTMLElement { _wired?: boolean } const bgdWindow = window as unknown as BackgroundBridge; @@ -456,7 +459,11 @@ const bgdWindow = window as unknown as BackgroundBridge; } } - bgdWindow.initBackgroundDecode = initBackgroundDecode; - bgdWindow.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents; - bgdWindow.setBackgroundDecodeRig = setBackgroundDecodeRig; + bgdWindow.trx ??= {}; + bgdWindow.trx.modules ??= {}; + bgdWindow.trx.modules.backgroundDecode = { + initialize: initBackgroundDecode, + wireEvents: wireBackgroundDecodeEvents, + setRig: setBackgroundDecodeRig, + }; })(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/bookmarks.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/bookmarks.ts index 48a0ba34..6a3942b3 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/bookmarks.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/bookmarks.ts @@ -35,6 +35,14 @@ interface BookmarkService { refreshOverlay(): Promise; invalidateColors(): void; apply(bookmark: Bookmark): void; + formatFrequency(frequencyHz: number): string; + fetch(categoryFilter: string): Promise; + populateScopePicker(): void; +} +interface VirtualChannelService { + interceptMode(mode: string): Promise; + interceptBandwidth(bandwidthHz: number): Promise; + takeSchedulerControl(): Promise; } interface BookmarkBridge extends Window { @@ -49,7 +57,7 @@ interface BookmarkBridge extends Window { currentBandwidthHz?: number; modeEl?: HTMLSelectElement | null; decoderRegistry?: DecoderDescriptor[]; - trx?: { modules?: { bookmarks?: BookmarkService } }; + trx?: { modules?: { bookmarks?: BookmarkService; vchan?: VirtualChannelService } }; trxUi: { confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise; notify?(message: string, options: { kind: "error" }): void; @@ -58,9 +66,6 @@ interface BookmarkBridge extends Window { scheduleSpectrumDraw?(): void; syncBandwidthInput?(bandwidthHz: number): void; applyLocalTunedFrequency?(frequencyHz: number, force?: boolean): void; - vchanTakeSchedulerControl?(): Promise; - vchanInterceptMode?(mode: string): Promise; - vchanInterceptBandwidth?(bandwidthHz: number): Promise; setRigFrequency?(frequencyHz: number): Promise; postPath(path: string): Promise; onDecoderRegistryReady?(callback: () => void): void; @@ -503,19 +508,16 @@ function bmApply(bm: Bookmark): void { // Take scheduler control up front, then apply mode before bandwidth so a // late SetMode cannot revert a saved WFM bookmark bandwidth to 180 kHz. const tunePromise = (async () => { - if (typeof bridge.vchanTakeSchedulerControl === "function") { - await bridge.vchanTakeSchedulerControl(); - } + await bridge.trx?.modules?.vchan?.takeSchedulerControl(); - const onVirtual = typeof bridge.vchanInterceptMode === "function" - && await bridge.vchanInterceptMode(bm.mode); + const onVirtual = await bridge.trx?.modules?.vchan?.interceptMode(bm.mode) ?? false; if (!onVirtual) { await bridge.postPath("/set_mode?mode=" + encodeURIComponent(bm.mode)); } if (bm.bandwidth_hz) { - const bwHandledByVchan = typeof bridge.vchanInterceptBandwidth === "function" - && await bridge.vchanInterceptBandwidth(bm.bandwidth_hz); + const bwHandledByVchan = + await bridge.trx?.modules?.vchan?.interceptBandwidth(bm.bandwidth_hz) ?? false; if (!bwHandledByVchan) { await bridge.postPath(`/set_bandwidth?hz=${bm.bandwidth_hz}`); } @@ -590,6 +592,9 @@ bridge.trx.modules.bookmarks = { refreshOverlay: bmFetchOverlay, invalidateColors() { bmOverlayRevision += 1; }, apply: bmApply, + formatFrequency: bmFmtFreq, + fetch: bmFetch, + populateScopePicker: bmPopulateScopePicker, }; function bmUpdateSelectionUi() { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler-types.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler-types.ts index bb6caa76..8c936168 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler-types.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler-types.ts @@ -69,9 +69,13 @@ export interface SchedulerService { getBookmarks(): SchedulerBookmark[]; markDirty(): void; } +interface VirtualChannelService { + takeSchedulerControl(): Promise; + releaseToScheduler(): Promise; +} export interface SchedulerWindow extends Window { - trx: { modules: { scheduler?: SchedulerService } }; + trx: { modules: { scheduler?: SchedulerService; vchan?: VirtualChannelService } }; trxUi: { confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise; notify?(message: string, options: { kind: "error" }): void; @@ -80,7 +84,5 @@ export interface SchedulerWindow extends Window { lastActiveRigId?: string | null; serverLat?: number | null; serverLon?: number | null; - vchanTakeSchedulerControl?(): Promise; - vchanToggleSchedulerRelease?(): Promise; satScheduler?: SatelliteSchedulerApi; } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler.ts index a97789bc..ff96351c 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/scheduler.ts @@ -1137,14 +1137,14 @@ function schedulerEl(id: string): SchedulerElement { schedulerStepPending = true; renderSchedulerStepControls(); - Promise.resolve(schedulerWindow.vchanTakeSchedulerControl?.() ?? null) + Promise.resolve(schedulerWindow.trx.modules.vchan?.takeSchedulerControl() ?? null) .then(function () { return apiActivateSchedulerEntry(rigId, targetId); }) .then(function (status) { currentSchedulerStatus = status || null; return Promise.resolve( - schedulerWindow.vchanToggleSchedulerRelease?.() ?? null + schedulerWindow.trx.modules.vchan?.releaseToScheduler() ?? null ).then(function () { renderStatus(status); renderSchedulerInterleaveStatus(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/vchan.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/vchan.ts index e6369d9b..6fe984b8 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/vchan.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/vchan.ts @@ -50,13 +50,21 @@ interface VirtualChannelBridge { applyLocalTunedFrequency?: (frequencyHz: number) => void; setRigFrequency?: (frequencyHz: number) => void; refreshFreqDisplay?: () => void; - vchanTakeSchedulerControl?: () => Promise; - vchanInterceptMode?: (mode: string) => Promise; - vchanInterceptBandwidth?: (bandwidthHz: number) => Promise; - vchanHandleSession?: (data: string) => void; - vchanHandleChannels?: (data: string) => void; - vchanApplyCapabilities?: (capabilities: { filter_controls?: boolean } | null) => void; - vchanIsOnVirtual?: () => boolean; + trx?: { modules?: { vchan?: VirtualChannelService } }; +} + +interface VirtualChannelService { + readonly channels: readonly VirtualChannel[]; + readonly activeId: string | null; + activeChannel(): VirtualChannel | null; + applyCapabilities(capabilities: { filter_controls?: boolean } | null): void; + handleSession(data: string): void; + handleChannels(data: string): void; + isOnVirtual(): boolean; + interceptMode(mode: string): Promise; + interceptBandwidth(bandwidthHz: number): Promise; + takeSchedulerControl(): Promise; + releaseToScheduler(): Promise; } const vchanWindow = window as unknown as VirtualChannelBridge; @@ -169,7 +177,6 @@ async function vchanTakeSchedulerControl() { console.error("scheduler control takeover failed", e); } } -vchanWindow.vchanTakeSchedulerControl = vchanTakeSchedulerControl; // Called by app.js when the SSE `session` event arrives. function vchanHandleSession(data: string): void { @@ -208,8 +215,6 @@ function vchanHandleChannels(data: string): void { console.warn("vchan: bad channels event", e); } } -vchanWindow.vchanHandleSession = vchanHandleSession; -vchanWindow.vchanHandleChannels = vchanHandleChannels; function vchanRender() { const picker = document.getElementById("vchan-picker"); @@ -388,7 +393,6 @@ function vchanApplyCapabilities(caps: { filter_controls?: boolean } | null): voi picker.style.display = (caps && caps.filter_controls) ? "" : "none"; vchanRenderSchedulerRelease(); } -vchanWindow.vchanApplyCapabilities = vchanApplyCapabilities; // --------------------------------------------------------------------------- // Freq / mode interception + UI accent @@ -399,7 +403,6 @@ function vchanIsOnVirtual(): boolean { if (!vchanActiveId || vchanChannels.length === 0) return false; return vchanActiveId !== vchanChannels[0]?.id; } -vchanWindow.vchanIsOnVirtual = vchanIsOnVirtual; function vchanActiveChannel(): VirtualChannel | null { return vchanChannels.find(c => c.id === vchanActiveId) || null; @@ -554,18 +557,34 @@ async function vchanSetChannelMode(mode: string): Promise { // Called by app.js (applyModeFromPicker) and bookmarks.js (bmApply) before // sending /set_mode to the server. Returns true if the change was handled // by the virtual channel (caller should skip the server request). -vchanWindow.vchanInterceptMode = async function(mode: string) { +async function vchanInterceptMode(mode: string): Promise { if (!vchanIsOnVirtual()) return false; await vchanSetChannelMode(mode); return true; -}; +} // Called by app.js bandwidth setters before sending /set_bandwidth to the // server. Returns true if the change was handled by the virtual channel. -vchanWindow.vchanInterceptBandwidth = async function(bwHz: number) { +async function vchanInterceptBandwidth(bwHz: number): Promise { if (!vchanIsOnVirtual()) return false; await vchanSetChannelBandwidth(bwHz); return true; +} + +vchanWindow.trx ??= {}; +vchanWindow.trx.modules ??= {}; +vchanWindow.trx.modules.vchan = { + get channels() { return vchanChannels; }, + get activeId() { return vchanActiveId; }, + activeChannel: vchanActiveChannel, + applyCapabilities: vchanApplyCapabilities, + handleSession: vchanHandleSession, + handleChannels: vchanHandleChannels, + isOnVirtual: vchanIsOnVirtual, + interceptMode: vchanInterceptMode, + interceptBandwidth: vchanInterceptBandwidth, + takeSchedulerControl: vchanTakeSchedulerControl, + releaseToScheduler: vchanToggleSchedulerRelease, }; // Wrap setRigFrequency (defined in app.js, loaded before this file) so that diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs index 2b69346b..7a7f91d3 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs @@ -38,9 +38,10 @@ test("background decode loads configuration for the explicitly selected rig", as const source = await readFile(new URL("../../assets/web/generated/background-decode.js", import.meta.url), "utf8"); new vm.Script(source).runInContext(context); - window.initBackgroundDecode("rig/a", "control"); + window.trx.modules.backgroundDecode.initialize("rig/a", "control"); await new Promise((resolve) => setTimeout(resolve, 0)); assert.ok(requested.includes("/background-decode/rig%2Fa")); assert.ok(requested.includes("/bookmarks")); assert.ok(requested.includes("/background-decode/rig%2Fa/status")); + assert.equal(window.initBackgroundDecode, undefined); }); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/bookmarks.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/bookmarks.test.mjs index 8ec76c1e..15b9440b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/bookmarks.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/bookmarks.test.mjs @@ -69,5 +69,7 @@ test("bookmarks register an explicit typed service for application consumers", a assert.equal(window.trx.modules.bookmarks.overlayList.length, 1); assert.equal(window.trx.modules.bookmarks.overlayList[0].id, "one"); assert.equal(typeof window.trx.modules.bookmarks.apply, "function"); + assert.equal(typeof window.trx.modules.bookmarks.formatFrequency, "function"); + assert.equal(typeof window.trx.modules.bookmarks.populateScopePicker, "function"); assert.equal(globalThis.bmOverlayList, undefined); }); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/vchan.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/vchan.test.mjs index a0f5d10c..b555f8e5 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/vchan.test.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/vchan.test.mjs @@ -28,9 +28,12 @@ test("virtual channels expose typed SSE and interception boundaries", async () = const source = await readFile(new URL("../../assets/web/generated/vchan.js", import.meta.url), "utf8"); new vm.Script(source).runInContext(context); - assert.equal(typeof window.vchanHandleSession, "function"); - assert.equal(typeof window.vchanHandleChannels, "function"); - assert.equal(typeof window.vchanApplyCapabilities, "function"); - assert.equal(await window.vchanInterceptMode("USB"), false); - assert.equal(await window.vchanInterceptBandwidth(2400), false); + const service = window.trx.modules.vchan; + assert.equal(typeof service.handleSession, "function"); + assert.equal(typeof service.handleChannels, "function"); + assert.equal(typeof service.applyCapabilities, "function"); + assert.equal(await service.interceptMode("USB"), false); + assert.equal(await service.interceptBandwidth(2400), false); + assert.equal(window.vchanHandleSession, undefined); + assert.equal(window.vchanInterceptBandwidth, undefined); });