Complete TypeScript frontend migration #22
@@ -1258,7 +1258,7 @@ function applyRigList(activeRigId, rigIds, displayNames) {
|
||||
updateRigIdentitySummary(lastActiveRigId);
|
||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||
if (rigListChanged) {
|
||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||
@@ -3593,7 +3593,7 @@ async function switchRigFromSelect(selectEl) {
|
||||
updateRigSubtitle(lastActiveRigId);
|
||||
updateRigIdentitySummary(lastActiveRigId);
|
||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||
window.trx.modules.map?.syncAprsReceiverMarker();
|
||||
@@ -4333,10 +4333,8 @@ async function initializeApp() {
|
||||
}
|
||||
}
|
||||
function initSettingsUI() {
|
||||
if (typeof initScheduler === "function") {
|
||||
initScheduler(lastActiveRigId, authRole);
|
||||
wireSchedulerEvents();
|
||||
}
|
||||
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
||||
window.trx.modules.scheduler?.wireEvents();
|
||||
if (typeof initBackgroundDecode === "function") {
|
||||
initBackgroundDecode(lastActiveRigId, authRole);
|
||||
wireBackgroundDecodeEvents();
|
||||
|
||||
@@ -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", "/sat-scheduler.js", "/vchan.js", "/bookmarks.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", "/vchan.js", "/bookmarks.js", "/scheduler.js"]);
|
||||
function loadLegacyScript(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
|
||||
@@ -24,8 +24,9 @@
|
||||
centerHz: document.getElementById("scheduler-sat-center-hz")
|
||||
};
|
||||
let editIdx = null;
|
||||
let eventsWired = false;
|
||||
function getBridge() {
|
||||
return satSchedulerWindow.schedulerBridge ?? null;
|
||||
return satSchedulerWindow.trx?.modules?.scheduler ?? null;
|
||||
}
|
||||
function getConfig() {
|
||||
const b = getBridge();
|
||||
@@ -230,6 +231,8 @@
|
||||
if (dom.norad) dom.norad.value = parts[1] || "";
|
||||
}
|
||||
function wireEvents() {
|
||||
if (eventsWired) return;
|
||||
eventsWired = true;
|
||||
if (dom.enabled) {
|
||||
const enabledInput = dom.enabled;
|
||||
dom.enabled.addEventListener("change", function() {
|
||||
@@ -255,5 +258,9 @@
|
||||
renderPassStatus,
|
||||
collectSatelliteConfig
|
||||
};
|
||||
if (getBridge()) {
|
||||
wireEvents();
|
||||
renderSection();
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
+1188
-1160
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,6 @@ await build({
|
||||
"plugin-runtime": path.join(sourceDir, "plugin-runtime.ts"),
|
||||
screenshot: path.join(sourceDir, "screenshot.ts"),
|
||||
"webgl-renderer": path.join(sourceDir, "webgl-renderer.ts"),
|
||||
scheduler: path.join(sourceDir, "plugins", "scheduler.js"),
|
||||
},
|
||||
outdir: outputDir,
|
||||
bundle: false,
|
||||
@@ -52,6 +51,7 @@ await build({
|
||||
"sat-scheduler": path.join(sourceDir, "plugins", "sat-scheduler.ts"),
|
||||
vchan: path.join(sourceDir, "plugins", "vchan.ts"),
|
||||
bookmarks: path.join(sourceDir, "plugins", "bookmarks.ts"),
|
||||
scheduler: path.join(sourceDir, "plugins", "scheduler.ts"),
|
||||
},
|
||||
outdir: outputDir,
|
||||
bundle: true,
|
||||
|
||||
@@ -1269,7 +1269,7 @@ function applyRigList(activeRigId, rigIds, displayNames) {
|
||||
updateRigIdentitySummary(lastActiveRigId);
|
||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||
if (rigListChanged) {
|
||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||
@@ -3935,7 +3935,7 @@ async function switchRigFromSelect(selectEl) {
|
||||
updateRigSubtitle(lastActiveRigId);
|
||||
updateRigIdentitySummary(lastActiveRigId);
|
||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||
window.trx.modules.map?.syncAprsReceiverMarker();
|
||||
@@ -4759,10 +4759,8 @@ async function initializeApp() {
|
||||
}
|
||||
|
||||
function initSettingsUI() {
|
||||
if (typeof initScheduler === "function") {
|
||||
initScheduler(lastActiveRigId, authRole);
|
||||
wireSchedulerEvents();
|
||||
}
|
||||
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
||||
window.trx.modules.scheduler?.wireEvents();
|
||||
if (typeof initBackgroundDecode === "function") {
|
||||
initBackgroundDecode(lastActiveRigId, authRole);
|
||||
wireBackgroundDecodeEvents();
|
||||
|
||||
@@ -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", "/sat-scheduler.js", "/vchan.js", "/bookmarks.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", "/vchan.js", "/bookmarks.js", "/scheduler.js"]);
|
||||
|
||||
function loadLegacyScript(path: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
+13
-8
@@ -7,13 +7,12 @@ import type {
|
||||
SatelliteScheduleConfig,
|
||||
SatelliteScheduleEntry,
|
||||
SatelliteSchedulerApi,
|
||||
SatelliteSchedulerBridge,
|
||||
SatelliteSchedulerStatus,
|
||||
SchedulerConfigWithSatellites,
|
||||
} from "./satellite-types";
|
||||
import type { SchedulerConfig, SchedulerService } from "./scheduler-types.js";
|
||||
|
||||
interface SatelliteSchedulerWindow {
|
||||
schedulerBridge?: SatelliteSchedulerBridge;
|
||||
trx?: { modules?: { scheduler?: SchedulerService } };
|
||||
satScheduler?: SatelliteSchedulerApi;
|
||||
trxUi?: { notify(message: string, options: { kind: "error" }): void };
|
||||
}
|
||||
@@ -49,15 +48,15 @@ const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
|
||||
|
||||
// ── Local state ───────────────────────────────────────────────────
|
||||
let editIdx: number | null = null;
|
||||
let eventsWired = false;
|
||||
|
||||
// ── Scheduler bridge ──────────────────────────────────────────────
|
||||
// These accessors call into scheduler.js via window.schedulerBridge,
|
||||
// which is set up by scheduler.js after it initializes.
|
||||
function getBridge(): SatelliteSchedulerBridge | null {
|
||||
return satSchedulerWindow.schedulerBridge ?? null;
|
||||
// These accessors use the shared typed scheduler service when available.
|
||||
function getBridge(): SchedulerService | null {
|
||||
return satSchedulerWindow.trx?.modules?.scheduler ?? null;
|
||||
}
|
||||
|
||||
function getConfig(): SchedulerConfigWithSatellites | null {
|
||||
function getConfig(): SchedulerConfig | null {
|
||||
const b = getBridge();
|
||||
return b?.getConfig() ?? null;
|
||||
}
|
||||
@@ -309,6 +308,8 @@ const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
|
||||
|
||||
// ── Wire all events ───────────────────────────────────────────────
|
||||
function wireEvents() {
|
||||
if (eventsWired) return;
|
||||
eventsWired = true;
|
||||
if (dom.enabled) {
|
||||
const enabledInput = dom.enabled;
|
||||
dom.enabled.addEventListener("change", function () {
|
||||
@@ -334,4 +335,8 @@ const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
|
||||
renderPassStatus: renderPassStatus,
|
||||
collectSatelliteConfig: collectSatelliteConfig,
|
||||
};
|
||||
if (getBridge()) {
|
||||
wireEvents();
|
||||
renderSection();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import type { SatelliteScheduleConfig, SatelliteSchedulerApi } from "./satellite-types.js";
|
||||
|
||||
export type SchedulerMode = "disabled" | "grayline" | "time_span";
|
||||
|
||||
export interface SchedulerBookmark {
|
||||
id: string;
|
||||
name: string;
|
||||
freq_hz: number;
|
||||
mode: string;
|
||||
}
|
||||
|
||||
export interface GraylineConfig {
|
||||
lat: number;
|
||||
lon: number;
|
||||
transition_window_min: number;
|
||||
day_bookmark_id: string | null;
|
||||
night_bookmark_id: string | null;
|
||||
dawn_bookmark_id: string | null;
|
||||
dusk_bookmark_id: string | null;
|
||||
}
|
||||
|
||||
export interface ScheduleEntry {
|
||||
id?: string;
|
||||
start_min: number;
|
||||
end_min: number;
|
||||
bookmark_id: string;
|
||||
label: string | null;
|
||||
interleave_min: number | null;
|
||||
center_hz: number | null;
|
||||
bookmark_ids: string[];
|
||||
record: boolean;
|
||||
exclusive?: boolean;
|
||||
}
|
||||
|
||||
export interface SchedulerConfig {
|
||||
remote: string | null;
|
||||
mode: SchedulerMode;
|
||||
grayline: GraylineConfig | null;
|
||||
entries: ScheduleEntry[];
|
||||
interleave_min?: number | null;
|
||||
satellites?: SatelliteScheduleConfig | null;
|
||||
}
|
||||
|
||||
export interface SchedulerStatus {
|
||||
active: boolean;
|
||||
last_entry_id?: string | null;
|
||||
last_bookmark_id?: string | null;
|
||||
last_bookmark_name?: string | null;
|
||||
last_applied_utc?: number | null;
|
||||
last_center_hz?: number | null;
|
||||
last_bookmark_ids?: string[];
|
||||
active_satellite?: string | null;
|
||||
freq_hz?: number | null;
|
||||
mode?: string | null;
|
||||
active_decoders?: string[];
|
||||
}
|
||||
|
||||
export interface SchedulerService {
|
||||
initialize(rigId: string | null, role: string | null): void;
|
||||
destroy(): void;
|
||||
setRig(rigId: string | null): void;
|
||||
wireEvents(): void;
|
||||
getConfig(): SchedulerConfig | null;
|
||||
getStatus(): SchedulerStatus | null;
|
||||
getBookmarks(): SchedulerBookmark[];
|
||||
markDirty(): void;
|
||||
}
|
||||
|
||||
export interface SchedulerWindow extends Window {
|
||||
trx: { modules: { scheduler?: SchedulerService } };
|
||||
trxUi: {
|
||||
confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise<boolean>;
|
||||
notify?(message: string, options: { kind: "error" }): void;
|
||||
};
|
||||
authRole?: string | null;
|
||||
lastActiveRigId?: string | null;
|
||||
serverLat?: number | null;
|
||||
serverLon?: number | null;
|
||||
vchanTakeSchedulerControl?(): Promise<void>;
|
||||
vchanToggleSchedulerRelease?(): Promise<unknown>;
|
||||
satScheduler?: SatelliteSchedulerApi;
|
||||
}
|
||||
+398
-349
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
// 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("scheduler registers a typed module service without lifecycle globals", async () => {
|
||||
const window = { trx: { modules: {} }, trxUi: { confirm: async () => true } };
|
||||
const context = vm.createContext({
|
||||
window,
|
||||
document: {
|
||||
activeElement: null,
|
||||
addEventListener() {},
|
||||
getElementById: () => null,
|
||||
querySelector: () => null,
|
||||
},
|
||||
localStorage: { getItem: () => null, setItem() {} },
|
||||
setInterval: () => 1,
|
||||
clearInterval() {},
|
||||
setTimeout: () => 1,
|
||||
HTMLElement: class HTMLElement {},
|
||||
Element: class Element {},
|
||||
Date,
|
||||
Number,
|
||||
String,
|
||||
Array,
|
||||
Promise,
|
||||
WeakSet,
|
||||
console,
|
||||
});
|
||||
const source = await readFile(new URL("../../assets/web/generated/scheduler.js", import.meta.url), "utf8");
|
||||
new vm.Script(source).runInContext(context);
|
||||
|
||||
const service = window.trx.modules.scheduler;
|
||||
assert.equal(typeof service.initialize, "function");
|
||||
assert.equal(typeof service.setRig, "function");
|
||||
assert.equal(typeof service.getConfig, "function");
|
||||
assert.equal(window.initScheduler, undefined);
|
||||
assert.equal(window.schedulerBridge, undefined);
|
||||
});
|
||||
Reference in New Issue
Block a user