Complete TypeScript frontend migration #22

Merged
sjg merged 51 commits from feat/typescript-frontend-migration into main 2026-08-01 23:06:19 +02:00
5 changed files with 57 additions and 12 deletions
Showing only changes of commit f87289b129 - Show all commits
@@ -1590,7 +1590,7 @@ async function loadPlugins(group) {
for (const path of pluginGroups[group]) await loadPlugin(path);
}
async function loadEagerPlugins() {
await Promise.all(["digital-modes", "map-data", "bookmarks", "settings"].map(loadPlugins));
await Promise.all(["digital-modes", "bookmarks", "settings"].map(loadPlugins));
}
async function loadPluginsForTab(tab) {
await loadPlugins(tab);
@@ -1892,7 +1892,7 @@ var centerFreqEl = requiredElement("center-freq");
var wavelengthEl = requiredElement("wavelength");
var sigStrengthEl = requiredElement("sig-strength");
var modeEl = requiredElement("mode");
var bandLabel = requiredElement("band-label");
var bandLabel = document.getElementById("band-label");
var powerBtn = requiredElement("power-btn");
var powerHint = requiredElement("power-hint");
var vfoPicker = requiredElement("vfo-picker");
@@ -1912,7 +1912,7 @@ var loadingEl = requiredElement("loading");
var contentEl = requiredElement("content");
var serverSubtitle = requiredElement("server-subtitle");
var rigSubtitle = requiredElement("rig-subtitle");
var ownerSubtitle = requiredElement("owner-subtitle");
var ownerSubtitle = document.getElementById("owner-subtitle");
var locationSubtitle = requiredElement("location-subtitle");
var loadingTitle = requiredElement("loading-title");
var loadingSub = requiredElement("loading-sub");
@@ -2241,7 +2241,7 @@ function setTheme(theme) {
invalidateBookmarkColors();
}
function invalidateBookmarkColors() {
const bookmarks = window.trx.modules.bookmarks;
const bookmarks = window.trx?.modules.bookmarks;
if (!bookmarks) return;
bookmarks.invalidateColors();
void getComputedStyle(document.documentElement).getPropertyValue("--bg");
@@ -26,8 +26,8 @@ npm run verify-generated
`npm run verify-generated` regenerates Rust wire contracts and browser bundles,
then rejects drift from the committed files. The browser smoke test needs a
local Chromium-family executable; set `CHROMIUM_PATH` when it is not installed
at a conventional system path.
local Chromium-family executable; set `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH`
when it is not installed at `/usr/bin/chromium`.
Cargo consumes committed output under `../assets/web/generated`. It never
invokes Node.js, installs packages, or accesses the network. After changing a
@@ -673,7 +673,7 @@ const centerFreqEl = requiredElement<HTMLInputElement>("center-freq");
const wavelengthEl = requiredElement("wavelength");
const sigStrengthEl = requiredElement("sig-strength");
const modeEl = requiredElement<HTMLSelectElement>("mode");
const bandLabel = requiredElement("band-label");
const bandLabel = document.getElementById("band-label");
const powerBtn = requiredElement<HTMLButtonElement>("power-btn");
const powerHint = requiredElement("power-hint");
const vfoPicker = requiredElement("vfo-picker");
@@ -693,7 +693,7 @@ const loadingEl = requiredElement("loading");
const contentEl = requiredElement("content");
const serverSubtitle = requiredElement("server-subtitle");
const rigSubtitle = requiredElement("rig-subtitle");
const ownerSubtitle = requiredElement("owner-subtitle");
const ownerSubtitle = document.getElementById("owner-subtitle");
const locationSubtitle = requiredElement("location-subtitle");
const loadingTitle = requiredElement("loading-title");
const loadingSub = requiredElement("loading-sub");
@@ -1063,7 +1063,7 @@ function setTheme(theme: string) {
// Recolour bookmark chips after any palette/theme change (setTheme or setStyle).
function invalidateBookmarkColors() {
const bookmarks = window.trx.modules.bookmarks;
const bookmarks = window.trx?.modules.bookmarks;
if (!bookmarks) return;
bookmarks.invalidateColors();
// Force the browser to recalculate styles so getComputedStyle reads new values.
@@ -37,7 +37,7 @@ async function loadPlugins(group: string): Promise<void> {
}
export async function loadEagerPlugins(): Promise<void> {
await Promise.all(["digital-modes", "map-data", "bookmarks", "settings"].map(loadPlugins));
await Promise.all(["digital-modes", "bookmarks", "settings"].map(loadPlugins));
}
export async function loadPluginsForTab(tab: string): Promise<void> {
@@ -17,6 +17,49 @@ const jsonRoutes = new Map([
["/auth/session", { authenticated: true, role: "control", auth_disabled: true }],
["/decoders", []],
["/rigs", { rigs: [], active_remote: null }],
["/status", {
info: {
manufacturer: "Smoke",
model: "Fixture",
revision: "1",
access: { Tcp: { addr: "127.0.0.1:0" } },
capabilities: {
min_freq_step_hz: 1,
supported_bands: [],
supported_modes: ["FM"],
num_vfos: 1,
lock: false,
lockable: false,
attenuator: false,
preamp: false,
rit: false,
rpt: false,
split: false,
tx: false,
tx_limit: false,
vfo_switch: false,
filter_controls: false,
signal_meter: false,
},
},
status: { freq: { hz: 100_000_000 }, mode: "FM", tx_en: false, vfo: null, tx: null, rx: null, lock: null },
band: null,
enabled: true,
initialized: true,
cw_auto: false,
cw_wpm: 20,
cw_tone_hz: 700,
aprs_decode_enabled: false,
hf_aprs_decode_enabled: false,
cw_decode_enabled: false,
ft8_decode_enabled: false,
ft4_decode_enabled: false,
ft2_decode_enabled: false,
wspr_decode_enabled: false,
lrpt_decode_enabled: false,
wefax_decode_enabled: false,
recorder_enabled: false,
}],
["/bandplan.json", {}],
["/api/recorder/status", []],
["/api/recorder/files", []],
@@ -80,10 +123,12 @@ const executablePath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
const browser = await chromium.launch({ executablePath, headless: true, args: ["--no-sandbox"] });
const page = await browser.newPage();
const runtimeErrors = [];
page.on("pageerror", (error) => runtimeErrors.push(error.message));
page.on("pageerror", (error) => runtimeErrors.push(error.stack ?? error.message));
try {
await page.goto(`http://127.0.0.1:${address.port}/`, { waitUntil: "networkidle" });
await page.goto(`http://127.0.0.1:${address.port}/`, { waitUntil: "domcontentloaded" });
await page.waitForTimeout(500);
assert.deepEqual(runtimeErrors, []);
await page.locator("#content").waitFor({ state: "visible" });
assert.equal(await page.locator("#auth-gate").isVisible(), false);
assert.equal(await page.locator("#tab-main").isVisible(), true);