Complete TypeScript frontend migration #22
@@ -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");
|
||||
|
||||
+247
-244
@@ -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, ">").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, ">").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 = '<span class="sch-sat-active-badge">PASS ACTIVE: ' + escHtml(status.active_satellite) + "</span>";
|
||||
} else {
|
||||
dom.passStatus.innerHTML = '<span style="color:var(--text-muted);font-size:0.8rem;">No satellite pass active. Predictions available in the SAT tab.</span>';
|
||||
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 = '<span class="sch-sat-active-badge">PASS ACTIVE: ' + escHtml(status.active_satellite) + "</span>";
|
||||
} else {
|
||||
dom.passStatus.innerHTML = '<span style="color:var(--text-muted);font-size:0.8rem;">No satellite pass active. Predictions available in the SAT tab.</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
function renderBookmarkSelect(selectedId) {
|
||||
if (!dom.bookmark) return;
|
||||
dom.bookmark.innerHTML = '<option value="">— none —</option>';
|
||||
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 = '<option value="">— none —</option>';
|
||||
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
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -16,7 +16,7 @@ const pluginGroups: Readonly<Record<PluginGroup, readonly string[]>> = {
|
||||
|
||||
const loaded = new Set<string>();
|
||||
const loading = new Map<string, Promise<void>>();
|
||||
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<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
+101
-85
@@ -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, ">")
|
||||
.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 =
|
||||
'<span class="sch-sat-active-badge">PASS ACTIVE: ' +
|
||||
@@ -196,37 +211,38 @@
|
||||
}
|
||||
|
||||
// ── Render: bookmark dropdown ─────────────────────────────────────
|
||||
function renderBookmarkSelect(selectedId) {
|
||||
if (!dom.bookmark) return;
|
||||
dom.bookmark.innerHTML = '<option value="">— none —</option>';
|
||||
function renderBookmarkSelect(selectedId: string | null): void {
|
||||
const bookmarkSelect = dom.bookmark;
|
||||
if (!bookmarkSelect) return;
|
||||
bookmarkSelect.innerHTML = '<option value="">— none —</option>';
|
||||
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,
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
|
||||
//
|
||||
// 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: [] },
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user