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 49ace0b9..09a3e166 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 @@ -72,61 +72,73 @@ } } - // src/app.js + // src/core/decoder-registry.ts + var bridge = window; + var readyCallbacks = []; var decoderRegistry = []; - window.decoderRegistry = decoderRegistry; - var _decoderRegistryReadyCallbacks = []; - window.onDecoderRegistryReady = function(fn) { - if (decoderRegistry.length > 0) fn(); - else _decoderRegistryReadyCallbacks.push(fn); - }; - (async function fetchDecoderRegistry() { - try { - const resp = await fetch("/decoders"); - if (resp.ok) { - decoderRegistry = await resp.json(); - window.decoderRegistry = decoderRegistry; - for (const fn of _decoderRegistryReadyCallbacks) fn(); - _decoderRegistryReadyCallbacks.length = 0; - hideUnsupportedDecoderTabs(); - refreshOperatorLayoutCapabilities(); - } - } catch (e) { - console.error("Failed to fetch decoder registry:", e); - } - })(); - function hideUnsupportedDecoderTabs() { - const knownIds = new Set(decoderRegistry.map(function(d) { - return d.id; - })); + function onDecoderRegistryReady(callback) { + if (decoderRegistry.length > 0) callback(); + else readyCallbacks.push(callback); + } + function hideUnsupportedDecoderUi() { + const knownIds = new Set(decoderRegistry.map(({ id }) => id)); const alwaysShow = /* @__PURE__ */ new Set(["overview", "rds", "sat"]); - document.querySelectorAll("#tab-digital-modes > .sub-tab-bar > .sub-tab[data-subtab]").forEach(function(btn) { - const id = btn.dataset.subtab; - if (alwaysShow.has(id) || knownIds.has(id)) return; - btn.style.display = "none"; - var panel = document.getElementById("subtab-" + id); + document.querySelectorAll( + "#tab-digital-modes > .sub-tab-bar > .sub-tab[data-subtab]" + ).forEach((button) => { + const id = button.dataset.subtab; + if (!id || alwaysShow.has(id) || knownIds.has(id)) return; + button.style.display = "none"; + const panel = document.getElementById(`subtab-${id}`); if (panel) panel.style.display = "none"; }); - document.querySelectorAll('[id^="about-dec-"]').forEach(function(el) { - var id = el.id.replace("about-dec-", ""); - if (!alwaysShow.has(id) && !knownIds.has(id)) { - var row = el.closest("tr"); - if (row) row.style.display = "none"; - } + document.querySelectorAll('[id^="about-dec-"]').forEach((element) => { + const id = element.id.replace("about-dec-", ""); + if (alwaysShow.has(id) || knownIds.has(id)) return; + const row = element.closest("tr"); + if (row) row.style.display = "none"; }); - document.querySelectorAll('[id^="settings-clear-"][id$="-history"]').forEach(function(el) { - var m = el.id.match(/^settings-clear-(.+)-history$/); - if (m && !alwaysShow.has(m[1]) && !knownIds.has(m[1])) { - el.style.display = "none"; - } + document.querySelectorAll('[id^="settings-clear-"][id$="-history"]').forEach((element) => { + const match = /^settings-clear-(.+)-history$/.exec(element.id); + const id = match?.[1]; + if (id && !alwaysShow.has(id) && !knownIds.has(id)) element.style.display = "none"; }); - document.querySelectorAll("#subtab-overview .plugin-item[data-decoder]").forEach(function(el) { - var id = el.dataset.decoder; - if (!alwaysShow.has(id) && !knownIds.has(id)) { - el.style.display = "none"; - } + document.querySelectorAll("#subtab-overview .plugin-item[data-decoder]").forEach((element) => { + const id = element.dataset.decoder; + if (id && !alwaysShow.has(id) && !knownIds.has(id)) element.style.display = "none"; }); } + function isDecoderDescriptor(value) { + if (typeof value !== "object" || value === null) return false; + const item = value; + return typeof item.id === "string" && typeof item.label === "string" && typeof item.activation === "string" && Array.isArray(item.active_modes) && item.active_modes.every((mode) => typeof mode === "string") && typeof item.background_decode === "boolean" && typeof item.bookmark_selectable === "boolean"; + } + function decodeRegistry(value) { + if (!Array.isArray(value) || !value.every(isDecoderDescriptor)) { + throw new TypeError("The decoder registry response is malformed"); + } + return value; + } + async function loadDecoderRegistry(onLoaded) { + try { + const response = await fetch("/decoders"); + if (!response.ok) return; + decoderRegistry = decodeRegistry(await response.json()); + bridge.decoderRegistry = decoderRegistry; + readyCallbacks.splice(0).forEach((callback) => { + callback(); + }); + hideUnsupportedDecoderUi(); + onLoaded(); + } catch (error) { + console.error("Failed to fetch decoder registry:", error); + } + } + bridge.decoderRegistry = decoderRegistry; + bridge.onDecoderRegistryReady = onDecoderRegistryReady; + + // src/app.js + void loadDecoderRegistry(refreshOperatorLayoutCapabilities); var authRole = null; var authEnabled = true; async function checkAuthStatus() { 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 7387afcc..7e11ca16 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 @@ -11,71 +11,13 @@ import { } from "./core/geo.js"; import { escapeHtml as escapeMapHtml } from "./core/dom.js"; import { loadSetting, saveSetting } from "./core/settings.js"; +import { + decoderRegistry, + loadDecoderRegistry, +} from "./core/decoder-registry.js"; // --- Decoder registry (fetched from /decoders on load) --- -/** @type {Array<{id:string,label:string,activation:string,active_modes:string[],background_decode:boolean,bookmark_selectable:boolean}>} */ -let decoderRegistry = []; -window.decoderRegistry = decoderRegistry; - -/** Callbacks invoked once the decoder registry is fetched. */ -const _decoderRegistryReadyCallbacks = []; -window.onDecoderRegistryReady = function (fn) { - if (decoderRegistry.length > 0) fn(); - else _decoderRegistryReadyCallbacks.push(fn); -}; - -(async function fetchDecoderRegistry() { - try { - const resp = await fetch("/decoders"); - if (resp.ok) { - decoderRegistry = await resp.json(); - window.decoderRegistry = decoderRegistry; - for (const fn of _decoderRegistryReadyCallbacks) fn(); - _decoderRegistryReadyCallbacks.length = 0; - hideUnsupportedDecoderTabs(); - refreshOperatorLayoutCapabilities(); - } - } catch (e) { - console.error("Failed to fetch decoder registry:", e); - } -})(); - -/** Hide decoder sub-tabs, panels, about rows, and settings buttons - * for decoders not present in the server's registry. */ -function hideUnsupportedDecoderTabs() { - const knownIds = new Set(decoderRegistry.map(function (d) { return d.id; })); - // Sub-tabs that are not decoders and should always be visible. - const alwaysShow = new Set(["overview", "rds", "sat"]); - document.querySelectorAll("#tab-digital-modes > .sub-tab-bar > .sub-tab[data-subtab]").forEach(function (btn) { - const id = btn.dataset.subtab; - if (alwaysShow.has(id) || knownIds.has(id)) return; - btn.style.display = "none"; - var panel = document.getElementById("subtab-" + id); - if (panel) panel.style.display = "none"; - }); - // About-tab decoder status rows: