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 cb434840..6a9a2707 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
@@ -50,6 +50,28 @@
return `${field1}${field2}${square1}${square2}${sub1}${sub2}`;
}
+ // src/core/dom.ts
+ function escapeHtml(input) {
+ return String(input).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
+ }
+
+ // src/core/settings.ts
+ var STORAGE_PREFIX2 = "trx_";
+ function saveSetting(key, value) {
+ try {
+ localStorage.setItem(`${STORAGE_PREFIX2}${key}`, JSON.stringify(value));
+ } catch {
+ }
+ }
+ function loadSetting(key, fallback) {
+ try {
+ const value = localStorage.getItem(`${STORAGE_PREFIX2}${key}`);
+ return value === null ? fallback : JSON.parse(value);
+ } catch {
+ return fallback;
+ }
+ }
+
// src/app.js
var decoderRegistry = [];
window.decoderRegistry = decoderRegistry;
@@ -105,24 +127,6 @@
}
});
}
- var STORAGE_PREFIX = "trx_";
- function saveSetting(key, value) {
- try {
- localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(value));
- } catch (e) {
- }
- }
- function loadSetting(key, fallback) {
- try {
- const v = localStorage.getItem(STORAGE_PREFIX + key);
- return v !== null ? JSON.parse(v) : fallback;
- } catch (e) {
- return fallback;
- }
- }
- function escapeMapHtml(input) {
- return String(input).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
- }
var authRole = null;
var authEnabled = true;
async function checkAuthStatus() {
@@ -1891,7 +1895,7 @@
const mainClass = hasPs ? "rds-ps-main" : "rds-ps-fallback";
const metaText = hasPs ? `${formatOverlayPi(rds?.pi)} · ${formatOverlayPty(rds?.pty, rds?.pty_name)}` : rds?.pty_name ?? (rds?.pty != null ? String(rds.pty) : "");
const trafficFlags = `${overlayTrafficFlagHtml("TP", rds?.traffic_program)}${overlayTrafficFlagHtml("TA", rds?.traffic_announcement)}`;
- return `${hasPs ? formatPsHtml(ps) : escapeMapHtml(mainText)}${escapeMapHtml(metaText)}${trafficFlags}`;
+ return `${hasPs ? formatPsHtml(ps) : escapeHtml(mainText)}${escapeHtml(metaText)}${trafficFlags}`;
}
function collectRdsOverlayEntries() {
const entries = [];
@@ -2901,7 +2905,7 @@ ${unsupportedBandSummary()}`;
if (titleEl) {
if (ownerWebsiteUrl) {
const label = ownerWebsiteName || displayLabelFromUrl(ownerWebsiteUrl);
- titleEl.innerHTML = `${escapeMapHtml(label)}`;
+ titleEl.innerHTML = `${escapeHtml(label)}`;
} else {
titleEl.textContent = serverVersion ? `trx-rs v${serverVersion}` : "trx-rs";
}
@@ -2994,14 +2998,14 @@ ${unsupportedBandSummary()}`;
}
if (serverSubtitle && update.server_callsign) {
const base = serverSubtitle.textContent.split(" hosted by")[0];
- const safeCallsign = escapeMapHtml(update.server_callsign);
+ const safeCallsign = escapeHtml(update.server_callsign);
const encodedCallsign = encodeURIComponent(update.server_callsign);
- serverSubtitle.innerHTML = `${escapeMapHtml(base)} hosted by ${safeCallsign}`;
+ serverSubtitle.innerHTML = `${escapeHtml(base)} hosted by ${safeCallsign}`;
}
updateRigSubtitle(lastActiveRigId);
if (ownerSubtitle) {
if (ownerCallsign) {
- const safeOwner = escapeMapHtml(ownerCallsign);
+ const safeOwner = escapeHtml(ownerCallsign);
const encodedOwner = encodeURIComponent(ownerCallsign);
ownerSubtitle.innerHTML = `Owner: ${safeOwner}`;
} else {
@@ -4549,7 +4553,7 @@ ${unsupportedBandSummary()}`;
saveSetting,
loadSetting,
showHint,
- escapeMapHtml,
+ escapeMapHtml: escapeHtml,
formatFreq,
formatFreqForHumans,
formatWavelength,
@@ -5591,7 +5595,7 @@ ${unsupportedBandSummary()}`;
for (const r of list) {
const started = new Date(r.started_at * 1e3).toLocaleTimeString();
const fname = r.path.split("/").pop();
- html += `
| ${escapeMapHtml(r.rig_id)} | ${r.vchan_id ? escapeMapHtml(r.vchan_id) : "-"} | ${escapeMapHtml(fname)} | ${started} |
`;
+ html += `| ${escapeHtml(r.rig_id)} | ${r.vchan_id ? escapeHtml(r.vchan_id) : "-"} | ${escapeHtml(fname)} | ${started} |
`;
}
html += "";
el.innerHTML = html;
@@ -5665,7 +5669,7 @@ ${unsupportedBandSummary()}`;
}
let html = '| File | Size | Actions |
';
for (const f of page) {
- const safeName = escapeMapHtml(f.name);
+ const safeName = escapeHtml(f.name);
const encodedName = encodeURIComponent(f.name);
html += '| ' + safeName + " | " + recorderFormatSize(f.size) + ' | |
';
}
@@ -6643,7 +6647,7 @@ ${unsupportedBandSummary()}`;
if (ch == null || ch === " ") {
html += `_`;
} else {
- html += escapeMapHtml(ch);
+ html += escapeHtml(ch);
}
}
return html;
@@ -7165,7 +7169,7 @@ ${unsupportedBandSummary()}`;
}
span.title = buildBookmarkTooltipText(bm) || bm.name + " — " + freqStr + (bm.comment ? "\n" + bm.comment : "");
span.dataset.bmId = bm.id;
- const labelHtml = options.sideStack ? `${escapeMapHtml(freqStr)}${escapeMapHtml(bm.name)}` : " " + escapeMapHtml(bm.name) + "";
+ const labelHtml = options.sideStack ? `${escapeHtml(freqStr)}${escapeHtml(bm.name)}` : " " + escapeHtml(bm.name) + "";
span.innerHTML = labelHtml;
const col = colorMap[bm.category || ""];
span.style.setProperty("--bm-cat-bg", col);
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 fc99a6ca..fb2b0a5e 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
@@ -9,6 +9,8 @@ import {
latLonToMaidenhead,
locatorToLatLon,
} from "./core/geo.js";
+import { escapeHtml as escapeMapHtml } from "./core/dom.js";
+import { loadSetting, saveSetting } from "./core/settings.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}>} */
@@ -75,25 +77,6 @@ function hideUnsupportedDecoderTabs() {
});
}
-// --- Persistent settings (localStorage) ---
-const STORAGE_PREFIX = "trx_";
-function saveSetting(key, value) {
- try { localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(value)); } catch(e) {}
-}
-function loadSetting(key, fallback) {
- try {
- const v = localStorage.getItem(STORAGE_PREFIX + key);
- return v !== null ? JSON.parse(v) : fallback;
- } catch(e) { return fallback; }
-}
-function escapeMapHtml(input) {
- return String(input)
- .replaceAll("&", "&")
- .replaceAll("<", "<")
- .replaceAll(">", ">")
- .replaceAll("\"", """);
-}
-
// --- Authentication ---
let authRole = null; // null (not authenticated), "rx" (read-only), or "control" (full access)
let authEnabled = true;
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/core/dom.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/core/dom.ts
new file mode 100644
index 00000000..a4aaf9a7
--- /dev/null
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/core/dom.ts
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: 2026 Stan Grams
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+export function escapeHtml(input: unknown): string {
+ return String(input)
+ .replaceAll("&", "&")
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll("\"", """);
+}
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/core/settings.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/core/settings.ts
new file mode 100644
index 00000000..2c116746
--- /dev/null
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/core/settings.ts
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: 2026 Stan Grams
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+const STORAGE_PREFIX = "trx_";
+
+export function saveSetting(key: string, value: unknown): void {
+ try {
+ localStorage.setItem(`${STORAGE_PREFIX}${key}`, JSON.stringify(value));
+ } catch {
+ // Storage can be unavailable in private browsing or under a strict policy.
+ }
+}
+
+export function loadSetting(key: string, fallback: T): T {
+ try {
+ const value = localStorage.getItem(`${STORAGE_PREFIX}${key}`);
+ return value === null ? fallback : JSON.parse(value) as T;
+ } catch {
+ return fallback;
+ }
+}