diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js
index 0547d1a1..8fd3ae18 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js
@@ -10,7 +10,7 @@ const pluginGroups = {
};
const loaded = /* @__PURE__ */ new Set();
const loading = /* @__PURE__ */ new Map();
-const modulePlugins = /* @__PURE__ */ new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js"]);
+const modulePlugins = /* @__PURE__ */ new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js", "/sat-scheduler.js"]);
function loadLegacyScript(path) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat-scheduler.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat-scheduler.js
index 47709cdd..fcd55ae2 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat-scheduler.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/sat-scheduler.js
@@ -1,256 +1,259 @@
"use strict";
-(function() {
- "use strict";
- const dom = {
- enabled: document.getElementById("scheduler-sat-enabled"),
- pretune: document.getElementById("scheduler-sat-pretune"),
- body: document.getElementById("scheduler-sat-body"),
- tbody: document.getElementById("scheduler-sat-tbody"),
- addBtn: document.getElementById("scheduler-sat-add-btn"),
- passStatus: document.getElementById("scheduler-sat-pass-status"),
- formWrap: document.getElementById("sch-sat-form-wrap"),
- formTitle: document.getElementById("sch-sat-form-title"),
- form: document.getElementById("sch-sat-form"),
- formCancel: document.getElementById("sch-sat-form-cancel"),
- preset: document.getElementById("scheduler-sat-preset"),
- name: document.getElementById("scheduler-sat-name"),
- norad: document.getElementById("scheduler-sat-norad"),
- bookmark: document.getElementById("scheduler-sat-bookmark"),
- minEl: document.getElementById("scheduler-sat-min-el"),
- priority: document.getElementById("scheduler-sat-priority"),
- centerHz: document.getElementById("scheduler-sat-center-hz")
- };
- let editIdx = null;
- function getBridge() {
- return window.schedulerBridge || {};
- }
- function getConfig() {
- const b = getBridge();
- return typeof b.getConfig === "function" ? b.getConfig() : null;
- }
- function getStatus() {
- const b = getBridge();
- return typeof b.getStatus === "function" ? b.getStatus() : null;
- }
- function getBookmarks() {
- const b = getBridge();
- return typeof b.getBookmarks === "function" ? b.getBookmarks() : [];
- }
- function markDirty() {
- var b = getBridge();
- if (typeof b.markDirty === "function") b.markDirty();
- }
- function bmName(id) {
- const bm = getBookmarks().find(function(b) {
- return b.id === id;
- });
- return bm ? bm.name : String(id || "");
- }
- function escHtml(s) {
- return String(s).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
- }
- function formatFreq(hz) {
- if (hz >= 1e6) return (hz / 1e6).toFixed(3) + " MHz";
- if (hz >= 1e3) return (hz / 1e3).toFixed(1) + " kHz";
- return hz + " Hz";
- }
- function getSatelliteEntries() {
- var config = getConfig();
- return config && config.satellites && Array.isArray(config.satellites.entries) ? config.satellites.entries : [];
- }
- function ensureSatelliteConfig() {
- var config = getConfig();
- if (!config) return { enabled: false, pretune_secs: 60, entries: [] };
- if (!config.satellites) config.satellites = { enabled: false, pretune_secs: 60, entries: [] };
- if (!config.satellites.entries) config.satellites.entries = [];
- return config.satellites;
- }
- function collectSatelliteConfig() {
- var enabled = dom.enabled ? dom.enabled.checked : false;
- var pretune = dom.pretune ? parseInt(dom.pretune.value, 10) : 60;
- return {
- enabled,
- pretune_secs: isNaN(pretune) || pretune < 0 ? 60 : pretune,
- entries: getSatelliteEntries()
+(() => {
+ // src/plugins/sat-scheduler.ts
+ var satSchedulerWindow = window;
+ (function() {
+ "use strict";
+ const dom = {
+ enabled: document.getElementById("scheduler-sat-enabled"),
+ pretune: document.getElementById("scheduler-sat-pretune"),
+ body: document.getElementById("scheduler-sat-body"),
+ tbody: document.getElementById("scheduler-sat-tbody"),
+ addBtn: document.getElementById("scheduler-sat-add-btn"),
+ passStatus: document.getElementById("scheduler-sat-pass-status"),
+ formWrap: document.getElementById("sch-sat-form-wrap"),
+ formTitle: document.getElementById("sch-sat-form-title"),
+ form: document.getElementById("sch-sat-form"),
+ formCancel: document.getElementById("sch-sat-form-cancel"),
+ preset: document.getElementById("scheduler-sat-preset"),
+ name: document.getElementById("scheduler-sat-name"),
+ norad: document.getElementById("scheduler-sat-norad"),
+ bookmark: document.getElementById("scheduler-sat-bookmark"),
+ minEl: document.getElementById("scheduler-sat-min-el"),
+ priority: document.getElementById("scheduler-sat-priority"),
+ centerHz: document.getElementById("scheduler-sat-center-hz")
};
- }
- function renderSection() {
- var config = getConfig();
- var satCfg = config && config.satellites || {};
- var enabled = !!satCfg.enabled;
- if (dom.enabled) dom.enabled.checked = enabled;
- if (dom.pretune) dom.pretune.value = satCfg.pretune_secs != null ? satCfg.pretune_secs : 60;
- if (dom.body) dom.body.style.display = enabled ? "" : "none";
- renderEntries();
- renderPassStatus();
- }
- function renderEntries() {
- if (!dom.tbody) return;
- var entries = getSatelliteEntries();
- var frag = document.createDocumentFragment();
- entries.forEach(function(entry, idx) {
- var tr = document.createElement("tr");
- var tdSat = document.createElement("td");
- tdSat.textContent = entry.satellite || "";
- tr.appendChild(tdSat);
- var tdNorad = document.createElement("td");
- tdNorad.textContent = entry.norad_id || "";
- tr.appendChild(tdNorad);
- var tdBm = document.createElement("td");
- tdBm.textContent = bmName(entry.bookmark_id);
- tr.appendChild(tdBm);
- var tdEl = document.createElement("td");
- tdEl.textContent = entry.min_elevation_deg != null ? entry.min_elevation_deg + "°" : "5°";
- tr.appendChild(tdEl);
- var tdPrio = document.createElement("td");
- tdPrio.textContent = entry.priority || 0;
- tr.appendChild(tdPrio);
- var tdActions = document.createElement("td");
- var editBtn = document.createElement("button");
- editBtn.className = "sch-write";
- editBtn.type = "button";
- editBtn.textContent = "Edit";
- editBtn.addEventListener("click", function() {
- openForm(entry, idx);
+ let editIdx = null;
+ function getBridge() {
+ return satSchedulerWindow.schedulerBridge ?? null;
+ }
+ function getConfig() {
+ const b = getBridge();
+ return b?.getConfig() ?? null;
+ }
+ function getStatus() {
+ const b = getBridge();
+ return b?.getStatus() ?? null;
+ }
+ function getBookmarks() {
+ const b = getBridge();
+ return b?.getBookmarks() ?? [];
+ }
+ function markDirty() {
+ getBridge()?.markDirty();
+ }
+ function bmName(id) {
+ const bm = getBookmarks().find(function(b) {
+ return b.id === id;
});
- tdActions.appendChild(editBtn);
- var removeBtn = document.createElement("button");
- removeBtn.className = "sch-write";
- removeBtn.type = "button";
- removeBtn.textContent = "Remove";
- removeBtn.addEventListener("click", function() {
- removeEntry(idx);
+ return bm ? bm.name : id;
+ }
+ function escHtml(s) {
+ return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
+ }
+ function formatFreq(hz) {
+ if (hz >= 1e6) return (hz / 1e6).toFixed(3) + " MHz";
+ if (hz >= 1e3) return (hz / 1e3).toFixed(1) + " kHz";
+ return `${String(hz)} Hz`;
+ }
+ function getSatelliteEntries() {
+ const config = getConfig();
+ return config && config.satellites && Array.isArray(config.satellites.entries) ? config.satellites.entries : [];
+ }
+ function ensureSatelliteConfig() {
+ const config = getConfig();
+ if (!config) return { enabled: false, pretune_secs: 60, entries: [] };
+ if (!config.satellites) config.satellites = { enabled: false, pretune_secs: 60, entries: [] };
+ return config.satellites;
+ }
+ function collectSatelliteConfig() {
+ const enabled = dom.enabled ? dom.enabled.checked : false;
+ const pretune = dom.pretune ? parseInt(dom.pretune.value, 10) : 60;
+ return {
+ enabled,
+ pretune_secs: isNaN(pretune) || pretune < 0 ? 60 : pretune,
+ entries: getSatelliteEntries()
+ };
+ }
+ function renderSection() {
+ const config = getConfig();
+ const satCfg = config?.satellites;
+ const enabled = satCfg?.enabled ?? false;
+ if (dom.enabled) dom.enabled.checked = enabled;
+ if (dom.pretune) dom.pretune.value = String(satCfg?.pretune_secs ?? 60);
+ if (dom.body) dom.body.style.display = enabled ? "" : "none";
+ renderEntries();
+ renderPassStatus();
+ }
+ function renderEntries() {
+ if (!dom.tbody) return;
+ const entries = getSatelliteEntries();
+ const frag = document.createDocumentFragment();
+ entries.forEach(function(entry, idx) {
+ const tr = document.createElement("tr");
+ const tdSat = document.createElement("td");
+ tdSat.textContent = entry.satellite || "";
+ tr.appendChild(tdSat);
+ const tdNorad = document.createElement("td");
+ tdNorad.textContent = String(entry.norad_id || "");
+ tr.appendChild(tdNorad);
+ const tdBm = document.createElement("td");
+ tdBm.textContent = bmName(entry.bookmark_id);
+ tr.appendChild(tdBm);
+ const tdEl = document.createElement("td");
+ tdEl.textContent = `${String(entry.min_elevation_deg)}°`;
+ tr.appendChild(tdEl);
+ const tdPrio = document.createElement("td");
+ tdPrio.textContent = String(entry.priority || 0);
+ tr.appendChild(tdPrio);
+ const tdActions = document.createElement("td");
+ const editBtn = document.createElement("button");
+ editBtn.className = "sch-write";
+ editBtn.type = "button";
+ editBtn.textContent = "Edit";
+ editBtn.addEventListener("click", function() {
+ openForm(entry, idx);
+ });
+ tdActions.appendChild(editBtn);
+ const removeBtn = document.createElement("button");
+ removeBtn.className = "sch-write";
+ removeBtn.type = "button";
+ removeBtn.textContent = "Remove";
+ removeBtn.addEventListener("click", function() {
+ removeEntry(idx);
+ });
+ tdActions.appendChild(removeBtn);
+ tr.appendChild(tdActions);
+ frag.appendChild(tr);
});
- tdActions.appendChild(removeBtn);
- tr.appendChild(tdActions);
- frag.appendChild(tr);
- });
- dom.tbody.replaceChildren(frag);
- }
- function renderPassStatus() {
- if (!dom.passStatus) return;
- var entries = getSatelliteEntries();
- if (entries.length === 0) {
- dom.passStatus.innerHTML = "";
- return;
+ dom.tbody.replaceChildren(frag);
}
- var status = getStatus();
- if (status && status.active_satellite) {
- dom.passStatus.innerHTML = 'PASS ACTIVE: ' + escHtml(status.active_satellite) + "";
- } else {
- dom.passStatus.innerHTML = 'No satellite pass active. Predictions available in the SAT tab.';
+ function renderPassStatus() {
+ if (!dom.passStatus) return;
+ const entries = getSatelliteEntries();
+ if (entries.length === 0) {
+ dom.passStatus.innerHTML = "";
+ return;
+ }
+ const status = getStatus();
+ if (status && status.active_satellite) {
+ dom.passStatus.innerHTML = 'PASS ACTIVE: ' + escHtml(status.active_satellite) + "";
+ } else {
+ dom.passStatus.innerHTML = 'No satellite pass active. Predictions available in the SAT tab.';
+ }
}
- }
- function renderBookmarkSelect(selectedId) {
- if (!dom.bookmark) return;
- dom.bookmark.innerHTML = '';
- getBookmarks().forEach(function(bm) {
- var opt = document.createElement("option");
- opt.value = bm.id;
- opt.textContent = bm.name + " (" + formatFreq(bm.freq_hz) + " " + bm.mode + ")";
- if (bm.id === selectedId) opt.selected = true;
- dom.bookmark.appendChild(opt);
- });
- }
- function removeEntry(idx) {
- var sat = ensureSatelliteConfig();
- sat.entries.splice(idx, 1);
- renderEntries();
- markDirty();
- }
- function openForm(entry, idx) {
- editIdx = idx != null ? idx : null;
- if (dom.formTitle) dom.formTitle.textContent = entry ? "Edit Satellite" : "Add Satellite";
- if (dom.preset) dom.preset.value = "";
- if (dom.name) dom.name.value = entry ? entry.satellite || "" : "";
- if (dom.norad) dom.norad.value = entry ? entry.norad_id || "" : "";
- if (dom.minEl) dom.minEl.value = entry && entry.min_elevation_deg != null ? entry.min_elevation_deg : 5;
- if (dom.priority) dom.priority.value = entry && entry.priority != null ? entry.priority : 0;
- if (dom.centerHz) dom.centerHz.value = entry && entry.center_hz ? entry.center_hz : "";
- renderBookmarkSelect(entry ? entry.bookmark_id : null);
- if (dom.formWrap) {
- dom.formWrap.style.display = "flex";
- if (dom.name) dom.name.focus();
+ function renderBookmarkSelect(selectedId) {
+ const bookmarkSelect = dom.bookmark;
+ if (!bookmarkSelect) return;
+ bookmarkSelect.innerHTML = '';
+ getBookmarks().forEach(function(bm) {
+ const opt = document.createElement("option");
+ opt.value = bm.id;
+ opt.textContent = bm.name + " (" + formatFreq(bm.freq_hz) + " " + bm.mode + ")";
+ if (bm.id === selectedId) opt.selected = true;
+ bookmarkSelect.appendChild(opt);
+ });
}
- }
- function closeForm() {
- if (dom.formWrap) dom.formWrap.style.display = "none";
- editIdx = null;
- }
- function onFormSubmit(e) {
- e.preventDefault();
- var satellite = dom.name ? dom.name.value.trim() : "";
- var noradId = dom.norad ? parseInt(dom.norad.value, 10) : NaN;
- var bmId = dom.bookmark ? dom.bookmark.value : "";
- if (!satellite) {
- window.trxUi?.notify("Enter a satellite name.", { kind: "error" });
- document.getElementById("scheduler-sat-name")?.focus();
- return;
+ function removeEntry(idx) {
+ const sat = ensureSatelliteConfig();
+ sat.entries.splice(idx, 1);
+ renderEntries();
+ markDirty();
}
- if (isNaN(noradId) || noradId <= 0) {
- window.trxUi?.notify("Enter a valid NORAD catalog number.", { kind: "error" });
- document.getElementById("scheduler-sat-norad")?.focus();
- return;
+ function openForm(entry, idx) {
+ editIdx = idx != null ? idx : null;
+ if (dom.formTitle) dom.formTitle.textContent = entry ? "Edit Satellite" : "Add Satellite";
+ if (dom.preset) dom.preset.value = "";
+ if (dom.name) dom.name.value = entry ? entry.satellite || "" : "";
+ if (dom.norad) dom.norad.value = entry ? String(entry.norad_id || "") : "";
+ if (dom.minEl) dom.minEl.value = String(entry?.min_elevation_deg ?? 5);
+ if (dom.priority) dom.priority.value = String(entry?.priority ?? 0);
+ if (dom.centerHz) dom.centerHz.value = entry?.center_hz ? String(entry.center_hz) : "";
+ renderBookmarkSelect(entry ? entry.bookmark_id : null);
+ if (dom.formWrap) {
+ dom.formWrap.style.display = "flex";
+ if (dom.name) dom.name.focus();
+ }
}
- if (!bmId) {
- window.trxUi?.notify("Select a bookmark.", { kind: "error" });
- document.getElementById("scheduler-sat-bookmark")?.focus();
- return;
+ function closeForm() {
+ if (dom.formWrap) dom.formWrap.style.display = "none";
+ editIdx = null;
}
- var minEl = dom.minEl ? parseFloat(dom.minEl.value) : 5;
- var prio = dom.priority ? parseInt(dom.priority.value, 10) : 0;
- var centerHzRaw = dom.centerHz ? parseInt(dom.centerHz.value, 10) : NaN;
- var sat = ensureSatelliteConfig();
- var entryData = {
- satellite,
- norad_id: noradId,
- bookmark_id: bmId,
- min_elevation_deg: isNaN(minEl) ? 5 : minEl,
- priority: isNaN(prio) ? 0 : prio,
- center_hz: !isNaN(centerHzRaw) && centerHzRaw > 0 ? centerHzRaw : null,
- bookmark_ids: []
+ function onFormSubmit(e) {
+ e.preventDefault();
+ const satellite = dom.name ? dom.name.value.trim() : "";
+ const noradId = dom.norad ? parseInt(dom.norad.value, 10) : NaN;
+ const bmId = dom.bookmark ? dom.bookmark.value : "";
+ if (!satellite) {
+ satSchedulerWindow.trxUi?.notify("Enter a satellite name.", { kind: "error" });
+ dom.name?.focus();
+ return;
+ }
+ if (isNaN(noradId) || noradId <= 0) {
+ satSchedulerWindow.trxUi?.notify("Enter a valid NORAD catalog number.", { kind: "error" });
+ dom.norad?.focus();
+ return;
+ }
+ if (!bmId) {
+ satSchedulerWindow.trxUi?.notify("Select a bookmark.", { kind: "error" });
+ dom.bookmark?.focus();
+ return;
+ }
+ const minEl = dom.minEl ? parseFloat(dom.minEl.value) : 5;
+ const prio = dom.priority ? parseInt(dom.priority.value, 10) : 0;
+ const centerHzRaw = dom.centerHz ? parseInt(dom.centerHz.value, 10) : NaN;
+ const sat = ensureSatelliteConfig();
+ const existing = editIdx !== null ? sat.entries[editIdx] : void 0;
+ const entryData = {
+ id: existing?.id ?? `sat_${Date.now().toString(36)}`,
+ satellite,
+ norad_id: noradId,
+ bookmark_id: bmId,
+ min_elevation_deg: isNaN(minEl) ? 5 : minEl,
+ priority: isNaN(prio) ? 0 : prio,
+ center_hz: !isNaN(centerHzRaw) && centerHzRaw > 0 ? centerHzRaw : null,
+ bookmark_ids: []
+ };
+ if (editIdx !== null) {
+ sat.entries[editIdx] = entryData;
+ } else {
+ sat.entries.push(entryData);
+ }
+ closeForm();
+ renderEntries();
+ markDirty();
+ }
+ function onPresetChange() {
+ if (!dom.preset || !dom.preset.value) return;
+ const parts = dom.preset.value.split("|");
+ if (dom.name) dom.name.value = parts[0] || "";
+ if (dom.norad) dom.norad.value = parts[1] || "";
+ }
+ function wireEvents() {
+ if (dom.enabled) {
+ const enabledInput = dom.enabled;
+ dom.enabled.addEventListener("change", function() {
+ if (dom.body) dom.body.style.display = enabledInput.checked ? "" : "none";
+ markDirty();
+ });
+ }
+ if (dom.pretune) {
+ dom.pretune.addEventListener("input", function() {
+ markDirty();
+ });
+ }
+ if (dom.addBtn) dom.addBtn.addEventListener("click", function() {
+ openForm(null, null);
+ });
+ if (dom.form) dom.form.addEventListener("submit", onFormSubmit);
+ if (dom.formCancel) dom.formCancel.addEventListener("click", closeForm);
+ if (dom.preset) dom.preset.addEventListener("change", onPresetChange);
+ }
+ satSchedulerWindow.satScheduler = {
+ wireEvents,
+ renderSection,
+ renderPassStatus,
+ collectSatelliteConfig
};
- if (editIdx !== null) {
- var existing = sat.entries[editIdx];
- entryData.id = existing ? existing.id : "sat_" + Date.now().toString(36);
- sat.entries[editIdx] = entryData;
- } else {
- entryData.id = "sat_" + Date.now().toString(36);
- sat.entries.push(entryData);
- }
- closeForm();
- renderEntries();
- markDirty();
- }
- function onPresetChange() {
- if (!dom.preset || !dom.preset.value) return;
- var parts = dom.preset.value.split("|");
- if (dom.name) dom.name.value = parts[0] || "";
- if (dom.norad) dom.norad.value = parts[1] || "";
- }
- function wireEvents() {
- if (dom.enabled) {
- dom.enabled.addEventListener("change", function() {
- if (dom.body) dom.body.style.display = dom.enabled.checked ? "" : "none";
- markDirty();
- });
- }
- if (dom.pretune) {
- dom.pretune.addEventListener("input", function() {
- markDirty();
- });
- }
- if (dom.addBtn) dom.addBtn.addEventListener("click", function() {
- openForm(null, null);
- });
- if (dom.form) dom.form.addEventListener("submit", onFormSubmit);
- if (dom.formCancel) dom.formCancel.addEventListener("click", closeForm);
- if (dom.preset) dom.preset.addEventListener("change", onPresetChange);
- }
- window.satScheduler = {
- wireEvents,
- renderSection,
- renderPassStatus,
- collectSatelliteConfig
- };
+ })();
})();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs
index e64d8b7f..83f0d9ed 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs
@@ -23,7 +23,6 @@ await build({
screenshot: path.join(sourceDir, "screenshot.ts"),
"webgl-renderer": path.join(sourceDir, "webgl-renderer.ts"),
bookmarks: path.join(sourceDir, "plugins", "bookmarks.js"),
- "sat-scheduler": path.join(sourceDir, "plugins", "sat-scheduler.js"),
scheduler: path.join(sourceDir, "plugins", "scheduler.js"),
vchan: path.join(sourceDir, "plugins", "vchan.js"),
},
@@ -51,6 +50,7 @@ await build({
aprs: path.join(sourceDir, "plugins", "aprs.ts"),
"hf-aprs": path.join(sourceDir, "plugins", "hf-aprs.ts"),
sat: path.join(sourceDir, "plugins", "sat.ts"),
+ "sat-scheduler": path.join(sourceDir, "plugins", "sat-scheduler.ts"),
},
outdir: outputDir,
bundle: true,
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts
index 1c839079..0de2e53d 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts
@@ -16,7 +16,7 @@ const pluginGroups: Readonly> = {
const loaded = new Set();
const loading = new Map>();
-const modulePlugins = new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js"]);
+const modulePlugins = new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js", "/sat-scheduler.js"]);
function loadLegacyScript(path: string): Promise {
return new Promise((resolve, reject) => {
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat-scheduler.js b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat-scheduler.ts
similarity index 62%
rename from src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat-scheduler.js
rename to src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat-scheduler.ts
index 166ff0e9..61ba946e 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat-scheduler.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/sat-scheduler.ts
@@ -2,6 +2,23 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
+import type {
+ SatelliteBookmark,
+ SatelliteScheduleConfig,
+ SatelliteScheduleEntry,
+ SatelliteSchedulerApi,
+ SatelliteSchedulerBridge,
+ SatelliteSchedulerStatus,
+ SchedulerConfigWithSatellites,
+} from "./satellite-types";
+
+interface SatelliteSchedulerWindow {
+ schedulerBridge?: SatelliteSchedulerBridge;
+ satScheduler?: SatelliteSchedulerApi;
+ trxUi?: { notify(message: string, options: { kind: "error" }): void };
+}
+const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
+
// Satellite Pass Scheduling UI
// Manages the satellite overlay section within the background decoding scheduler.
// Communicates with scheduler.js via a thin window API for shared state access.
@@ -11,93 +28,91 @@
// ── DOM references (cached once) ──────────────────────────────────
const dom = {
- enabled: document.getElementById("scheduler-sat-enabled"),
- pretune: document.getElementById("scheduler-sat-pretune"),
+ enabled: document.getElementById("scheduler-sat-enabled") as HTMLInputElement | null,
+ pretune: document.getElementById("scheduler-sat-pretune") as HTMLInputElement | null,
body: document.getElementById("scheduler-sat-body"),
tbody: document.getElementById("scheduler-sat-tbody"),
addBtn: document.getElementById("scheduler-sat-add-btn"),
passStatus: document.getElementById("scheduler-sat-pass-status"),
formWrap: document.getElementById("sch-sat-form-wrap"),
formTitle: document.getElementById("sch-sat-form-title"),
- form: document.getElementById("sch-sat-form"),
+ form: document.getElementById("sch-sat-form") as HTMLFormElement | null,
formCancel: document.getElementById("sch-sat-form-cancel"),
- preset: document.getElementById("scheduler-sat-preset"),
- name: document.getElementById("scheduler-sat-name"),
- norad: document.getElementById("scheduler-sat-norad"),
- bookmark: document.getElementById("scheduler-sat-bookmark"),
- minEl: document.getElementById("scheduler-sat-min-el"),
- priority: document.getElementById("scheduler-sat-priority"),
- centerHz: document.getElementById("scheduler-sat-center-hz"),
+ preset: document.getElementById("scheduler-sat-preset") as HTMLSelectElement | null,
+ name: document.getElementById("scheduler-sat-name") as HTMLInputElement | null,
+ norad: document.getElementById("scheduler-sat-norad") as HTMLInputElement | null,
+ bookmark: document.getElementById("scheduler-sat-bookmark") as HTMLSelectElement | null,
+ minEl: document.getElementById("scheduler-sat-min-el") as HTMLInputElement | null,
+ priority: document.getElementById("scheduler-sat-priority") as HTMLInputElement | null,
+ centerHz: document.getElementById("scheduler-sat-center-hz") as HTMLInputElement | null,
};
// ── Local state ───────────────────────────────────────────────────
- let editIdx = null; // null = adding, number = editing
+ let editIdx: number | null = null;
// ── Scheduler bridge ──────────────────────────────────────────────
// These accessors call into scheduler.js via window.schedulerBridge,
// which is set up by scheduler.js after it initializes.
- function getBridge() {
- return window.schedulerBridge || {};
+ function getBridge(): SatelliteSchedulerBridge | null {
+ return satSchedulerWindow.schedulerBridge ?? null;
}
- function getConfig() {
+ function getConfig(): SchedulerConfigWithSatellites | null {
const b = getBridge();
- return typeof b.getConfig === "function" ? b.getConfig() : null;
+ return b?.getConfig() ?? null;
}
- function getStatus() {
+ function getStatus(): SatelliteSchedulerStatus | null {
const b = getBridge();
- return typeof b.getStatus === "function" ? b.getStatus() : null;
+ return b?.getStatus() ?? null;
}
- function getBookmarks() {
+ function getBookmarks(): SatelliteBookmark[] {
const b = getBridge();
- return typeof b.getBookmarks === "function" ? b.getBookmarks() : [];
+ return b?.getBookmarks() ?? [];
}
function markDirty() {
- var b = getBridge();
- if (typeof b.markDirty === "function") b.markDirty();
+ getBridge()?.markDirty();
}
- function bmName(id) {
+ function bmName(id: string): string {
const bm = getBookmarks().find(function (b) { return b.id === id; });
- return bm ? bm.name : String(id || "");
+ return bm ? bm.name : id;
}
- function escHtml(s) {
- return String(s)
+ function escHtml(s: string): string {
+ return s
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}
- function formatFreq(hz) {
+ function formatFreq(hz: number): string {
if (hz >= 1e6) return (hz / 1e6).toFixed(3) + " MHz";
if (hz >= 1e3) return (hz / 1e3).toFixed(1) + " kHz";
- return hz + " Hz";
+ return `${String(hz)} Hz`;
}
// ── Satellite config helpers ──────────────────────────────────────
- function getSatelliteEntries() {
- var config = getConfig();
+ function getSatelliteEntries(): SatelliteScheduleEntry[] {
+ const config = getConfig();
return (config && config.satellites && Array.isArray(config.satellites.entries))
? config.satellites.entries
: [];
}
- function ensureSatelliteConfig() {
- var config = getConfig();
+ function ensureSatelliteConfig(): SatelliteScheduleConfig {
+ const config = getConfig();
if (!config) return { enabled: false, pretune_secs: 60, entries: [] };
if (!config.satellites) config.satellites = { enabled: false, pretune_secs: 60, entries: [] };
- if (!config.satellites.entries) config.satellites.entries = [];
return config.satellites;
}
- function collectSatelliteConfig() {
- var enabled = dom.enabled ? dom.enabled.checked : false;
- var pretune = dom.pretune ? parseInt(dom.pretune.value, 10) : 60;
+ function collectSatelliteConfig(): SatelliteScheduleConfig {
+ const enabled = dom.enabled ? dom.enabled.checked : false;
+ const pretune = dom.pretune ? parseInt(dom.pretune.value, 10) : 60;
return {
enabled: enabled,
pretune_secs: isNaN(pretune) || pretune < 0 ? 60 : pretune,
@@ -107,12 +122,12 @@
// ── Render: section ───────────────────────────────────────────────
function renderSection() {
- var config = getConfig();
- var satCfg = (config && config.satellites) || {};
- var enabled = !!satCfg.enabled;
+ const config = getConfig();
+ const satCfg = config?.satellites;
+ const enabled = satCfg?.enabled ?? false;
if (dom.enabled) dom.enabled.checked = enabled;
- if (dom.pretune) dom.pretune.value = satCfg.pretune_secs != null ? satCfg.pretune_secs : 60;
+ if (dom.pretune) dom.pretune.value = String(satCfg?.pretune_secs ?? 60);
if (dom.body) dom.body.style.display = enabled ? "" : "none";
renderEntries();
@@ -122,35 +137,35 @@
// ── Render: entries table ─────────────────────────────────────────
function renderEntries() {
if (!dom.tbody) return;
- var entries = getSatelliteEntries();
- var frag = document.createDocumentFragment();
+ const entries = getSatelliteEntries();
+ const frag = document.createDocumentFragment();
entries.forEach(function (entry, idx) {
- var tr = document.createElement("tr");
+ const tr = document.createElement("tr");
- var tdSat = document.createElement("td");
+ const tdSat = document.createElement("td");
tdSat.textContent = entry.satellite || "";
tr.appendChild(tdSat);
- var tdNorad = document.createElement("td");
- tdNorad.textContent = entry.norad_id || "";
+ const tdNorad = document.createElement("td");
+ tdNorad.textContent = String(entry.norad_id || "");
tr.appendChild(tdNorad);
- var tdBm = document.createElement("td");
+ const tdBm = document.createElement("td");
tdBm.textContent = bmName(entry.bookmark_id);
tr.appendChild(tdBm);
- var tdEl = document.createElement("td");
- tdEl.textContent = (entry.min_elevation_deg != null ? entry.min_elevation_deg + "\u00B0" : "5\u00B0");
+ const tdEl = document.createElement("td");
+ tdEl.textContent = `${String(entry.min_elevation_deg)}\u00B0`;
tr.appendChild(tdEl);
- var tdPrio = document.createElement("td");
- tdPrio.textContent = entry.priority || 0;
+ const tdPrio = document.createElement("td");
+ tdPrio.textContent = String(entry.priority || 0);
tr.appendChild(tdPrio);
- var tdActions = document.createElement("td");
+ const tdActions = document.createElement("td");
- var editBtn = document.createElement("button");
+ const editBtn = document.createElement("button");
editBtn.className = "sch-write";
editBtn.type = "button";
editBtn.textContent = "Edit";
@@ -159,7 +174,7 @@
});
tdActions.appendChild(editBtn);
- var removeBtn = document.createElement("button");
+ const removeBtn = document.createElement("button");
removeBtn.className = "sch-write";
removeBtn.type = "button";
removeBtn.textContent = "Remove";
@@ -178,12 +193,12 @@
// ── Render: pass status ───────────────────────────────────────────
function renderPassStatus() {
if (!dom.passStatus) return;
- var entries = getSatelliteEntries();
+ const entries = getSatelliteEntries();
if (entries.length === 0) {
dom.passStatus.innerHTML = "";
return;
}
- var status = getStatus();
+ const status = getStatus();
if (status && status.active_satellite) {
dom.passStatus.innerHTML =
'PASS ACTIVE: ' +
@@ -196,37 +211,38 @@
}
// ── Render: bookmark dropdown ─────────────────────────────────────
- function renderBookmarkSelect(selectedId) {
- if (!dom.bookmark) return;
- dom.bookmark.innerHTML = '';
+ function renderBookmarkSelect(selectedId: string | null): void {
+ const bookmarkSelect = dom.bookmark;
+ if (!bookmarkSelect) return;
+ bookmarkSelect.innerHTML = '';
getBookmarks().forEach(function (bm) {
- var opt = document.createElement("option");
+ const opt = document.createElement("option");
opt.value = bm.id;
opt.textContent = bm.name + " (" + formatFreq(bm.freq_hz) + " " + bm.mode + ")";
if (bm.id === selectedId) opt.selected = true;
- dom.bookmark.appendChild(opt);
+ bookmarkSelect.appendChild(opt);
});
}
// ── Entry management ──────────────────────────────────────────────
- function removeEntry(idx) {
- var sat = ensureSatelliteConfig();
+ function removeEntry(idx: number): void {
+ const sat = ensureSatelliteConfig();
sat.entries.splice(idx, 1);
renderEntries();
markDirty();
}
// ── Form: open ────────────────────────────────────────────────────
- function openForm(entry, idx) {
+ function openForm(entry: SatelliteScheduleEntry | null, idx: number | null): void {
editIdx = (idx != null) ? idx : null;
if (dom.formTitle) dom.formTitle.textContent = entry ? "Edit Satellite" : "Add Satellite";
if (dom.preset) dom.preset.value = "";
if (dom.name) dom.name.value = entry ? (entry.satellite || "") : "";
- if (dom.norad) dom.norad.value = entry ? (entry.norad_id || "") : "";
- if (dom.minEl) dom.minEl.value = entry && entry.min_elevation_deg != null ? entry.min_elevation_deg : 5;
- if (dom.priority) dom.priority.value = entry && entry.priority != null ? entry.priority : 0;
- if (dom.centerHz) dom.centerHz.value = entry && entry.center_hz ? entry.center_hz : "";
+ if (dom.norad) dom.norad.value = entry ? String(entry.norad_id || "") : "";
+ if (dom.minEl) dom.minEl.value = String(entry?.min_elevation_deg ?? 5);
+ if (dom.priority) dom.priority.value = String(entry?.priority ?? 0);
+ if (dom.centerHz) dom.centerHz.value = entry?.center_hz ? String(entry.center_hz) : "";
renderBookmarkSelect(entry ? entry.bookmark_id : null);
@@ -243,24 +259,26 @@
}
// ── Form: submit ──────────────────────────────────────────────────
- function onFormSubmit(e) {
+ function onFormSubmit(e: SubmitEvent): void {
e.preventDefault();
- var satellite = dom.name ? dom.name.value.trim() : "";
- var noradId = dom.norad ? parseInt(dom.norad.value, 10) : NaN;
- var bmId = dom.bookmark ? dom.bookmark.value : "";
+ const satellite = dom.name ? dom.name.value.trim() : "";
+ const noradId = dom.norad ? parseInt(dom.norad.value, 10) : NaN;
+ const bmId = dom.bookmark ? dom.bookmark.value : "";
- if (!satellite) { window.trxUi?.notify("Enter a satellite name.", { kind: "error" }); document.getElementById("scheduler-sat-name")?.focus(); return; }
- if (isNaN(noradId) || noradId <= 0) { window.trxUi?.notify("Enter a valid NORAD catalog number.", { kind: "error" }); document.getElementById("scheduler-sat-norad")?.focus(); return; }
- if (!bmId) { window.trxUi?.notify("Select a bookmark.", { kind: "error" }); document.getElementById("scheduler-sat-bookmark")?.focus(); return; }
+ if (!satellite) { satSchedulerWindow.trxUi?.notify("Enter a satellite name.", { kind: "error" }); dom.name?.focus(); return; }
+ if (isNaN(noradId) || noradId <= 0) { satSchedulerWindow.trxUi?.notify("Enter a valid NORAD catalog number.", { kind: "error" }); dom.norad?.focus(); return; }
+ if (!bmId) { satSchedulerWindow.trxUi?.notify("Select a bookmark.", { kind: "error" }); dom.bookmark?.focus(); return; }
- var minEl = dom.minEl ? parseFloat(dom.minEl.value) : 5;
- var prio = dom.priority ? parseInt(dom.priority.value, 10) : 0;
- var centerHzRaw = dom.centerHz ? parseInt(dom.centerHz.value, 10) : NaN;
+ const minEl = dom.minEl ? parseFloat(dom.minEl.value) : 5;
+ const prio = dom.priority ? parseInt(dom.priority.value, 10) : 0;
+ const centerHzRaw = dom.centerHz ? parseInt(dom.centerHz.value, 10) : NaN;
- var sat = ensureSatelliteConfig();
+ const sat = ensureSatelliteConfig();
- var entryData = {
+ const existing = editIdx !== null ? sat.entries[editIdx] : undefined;
+ const entryData: SatelliteScheduleEntry = {
+ id: existing?.id ?? `sat_${Date.now().toString(36)}`,
satellite: satellite,
norad_id: noradId,
bookmark_id: bmId,
@@ -271,11 +289,8 @@
};
if (editIdx !== null) {
- var existing = sat.entries[editIdx];
- entryData.id = existing ? existing.id : ("sat_" + Date.now().toString(36));
sat.entries[editIdx] = entryData;
} else {
- entryData.id = "sat_" + Date.now().toString(36);
sat.entries.push(entryData);
}
@@ -287,7 +302,7 @@
// ── Preset change handler ─────────────────────────────────────────
function onPresetChange() {
if (!dom.preset || !dom.preset.value) return;
- var parts = dom.preset.value.split("|");
+ const parts = dom.preset.value.split("|");
if (dom.name) dom.name.value = parts[0] || "";
if (dom.norad) dom.norad.value = parts[1] || "";
}
@@ -295,8 +310,9 @@
// ── Wire all events ───────────────────────────────────────────────
function wireEvents() {
if (dom.enabled) {
+ const enabledInput = dom.enabled;
dom.enabled.addEventListener("change", function () {
- if (dom.body) dom.body.style.display = dom.enabled.checked ? "" : "none";
+ if (dom.body) dom.body.style.display = enabledInput.checked ? "" : "none";
markDirty();
});
}
@@ -312,7 +328,7 @@
}
// ── Public API ────────────────────────────────────────────────────
- window.satScheduler = {
+ satSchedulerWindow.satScheduler = {
wireEvents: wireEvents,
renderSection: renderSection,
renderPassStatus: renderPassStatus,
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/satellite-types.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/satellite-types.ts
index ad069e63..6eebb1cb 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/satellite-types.ts
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/satellite-types.ts
@@ -53,3 +53,31 @@ export interface SatelliteScheduleConfig {
pretune_secs: number;
entries: SatelliteScheduleEntry[];
}
+
+export interface SatelliteBookmark {
+ id: string;
+ name: string;
+ freq_hz: number;
+ mode: string;
+}
+
+export interface SchedulerConfigWithSatellites {
+ satellites?: SatelliteScheduleConfig;
+ [key: string]: unknown;
+}
+
+export interface SatelliteSchedulerStatus { active_satellite?: string | null }
+
+export interface SatelliteSchedulerBridge {
+ getConfig(): SchedulerConfigWithSatellites | null;
+ getStatus(): SatelliteSchedulerStatus | null;
+ getBookmarks(): SatelliteBookmark[];
+ markDirty(): void;
+}
+
+export interface SatelliteSchedulerApi {
+ wireEvents(): void;
+ renderSection(): void;
+ renderPassStatus(): void;
+ collectSatelliteConfig(): SatelliteScheduleConfig;
+}
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat-scheduler.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat-scheduler.test.mjs
new file mode 100644
index 00000000..6895e5db
--- /dev/null
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/sat-scheduler.test.mjs
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2026 Stan Grams
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+import assert from "node:assert/strict";
+import { readFile } from "node:fs/promises";
+import test from "node:test";
+import vm from "node:vm";
+
+test("satellite scheduler exposes a typed config adapter with safe defaults", async () => {
+ const window = {};
+ const context = vm.createContext({
+ window,
+ document: { getElementById: () => null, createDocumentFragment: () => ({ appendChild() {} }) },
+ Date,
+ Number,
+ String,
+ Array,
+ console,
+ });
+ const source = await readFile(new URL("../../assets/web/generated/sat-scheduler.js", import.meta.url), "utf8");
+ new vm.Script(source).runInContext(context);
+
+ assert.equal(typeof window.satScheduler.wireEvents, "function");
+ assert.deepEqual(
+ JSON.parse(JSON.stringify(window.satScheduler.collectSatelliteConfig())),
+ { enabled: false, pretune_secs: 60, entries: [] },
+ );
+});