CI / lint (pull_request) Successful in 2m16s
CI / frontend (pull_request) Successful in 4m12s
CI / reuse (pull_request) Successful in 2s
CI / lint (push) Successful in 2m15s
CI / test (pull_request) Successful in 9m37s
CI / test (push) Successful in 7m36s
CI / frontend (push) Failing after 31s
CI / reuse (push) Successful in 3s
Wires the SSTV decoder into the stack, from the audio the server already has to a panel in the browser that shows the picture arriving. Server: a decoder task alongside the WEFAX one, running whenever the decoder is enabled and the rig is in a mode SSTV is sent in. A finished picture is written to the cache as a PNG and sent on as a message; the rows are sent as they decode, so a client can watch two minutes of Martin M1 fill in rather than waiting for it. Pictures join the decode history, are replayed to a client that connects later, and survive a restart. Protocol: SetSstvDecodeEnabled and ResetSstvDecoder, a sstv_decode _enabled flag in the rig state, two audio message types, and Sstv and SstvProgress on DecodedMessage. The history stores the message without its base64 payload -- the picture is already on disk, and a megabyte per entry is not what a history is for. Client: pictures land in their own history, and the PNG the server sent is written to the local cache so /sstv-images/ can serve it back. That endpoint and the WEFAX one now share their filename checks rather than each carrying a copy: no separators, no parent references, .png only. Web UI: an SSTV sub-tab beside WEFAX, with a live canvas the rows paint into at the line number they carry, a card for the last picture, and a filterable history with links to the files. Rows below the one arriving are grey rather than black -- not yet received is a different thing from received as black. A picture is not a spot, so neither pictures nor their progress updates reach the decode statistics; that exclusion list had grown by hand for LRPT and WEFAX and is now one named set. The decoder crate gains what the server needed to hand a picture on: to_png, to_png_base64 and save_png, with file names stamped in UTC so they sort. Panel behaviour is tested with the plugin runtime: rows painting at their own line numbers rather than in arrival order, a completed picture linked by file name alone with no server path in the page, a cut-off picture reported as partial, clearing, and the toggle following the rig state. Signed-off-by: Stan Grams <sjg@haxx.space>
65 lines
2.2 KiB
JavaScript
65 lines
2.2 KiB
JavaScript
// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import { build } from "esbuild";
|
|
import { rm } from "node:fs/promises";
|
|
import { fileURLToPath } from "node:url";
|
|
import path from "node:path";
|
|
|
|
const frontendDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const sourceDir = path.join(frontendDir, "src");
|
|
const outputDir = path.join(frontendDir, "..", "assets", "web", "generated");
|
|
|
|
await rm(outputDir, { recursive: true, force: true });
|
|
|
|
await build({
|
|
entryPoints: {
|
|
app: path.join(sourceDir, "bootstrap.ts"),
|
|
ft2: path.join(sourceDir, "plugins", "ft2.ts"),
|
|
ft4: path.join(sourceDir, "plugins", "ft4.ts"),
|
|
wspr: path.join(sourceDir, "plugins", "wspr.ts"),
|
|
cw: path.join(sourceDir, "plugins", "cw.ts"),
|
|
ft8: path.join(sourceDir, "plugins", "ft8.ts"),
|
|
vdes: path.join(sourceDir, "plugins", "vdes.ts"),
|
|
wefax: path.join(sourceDir, "plugins", "wefax.ts"),
|
|
sstv: path.join(sourceDir, "plugins", "sstv.ts"),
|
|
"background-decode": path.join(sourceDir, "plugins", "background-decode.ts"),
|
|
ais: path.join(sourceDir, "plugins", "ais.ts"),
|
|
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"),
|
|
vchan: path.join(sourceDir, "plugins", "vchan.ts"),
|
|
bookmarks: path.join(sourceDir, "plugins", "bookmarks.ts"),
|
|
scheduler: path.join(sourceDir, "plugins", "scheduler.ts"),
|
|
"map-core": path.join(sourceDir, "map-core.ts"),
|
|
screenshot: path.join(sourceDir, "screenshot.ts"),
|
|
},
|
|
outdir: outputDir,
|
|
bundle: true,
|
|
format: "esm",
|
|
splitting: true,
|
|
entryNames: "[name]",
|
|
chunkNames: "chunk-[hash]",
|
|
platform: "browser",
|
|
target: "es2022",
|
|
sourcemap: false,
|
|
legalComments: "inline",
|
|
charset: "utf8",
|
|
logLevel: "info",
|
|
});
|
|
|
|
await build({
|
|
entryPoints: [path.join(sourceDir, "decode-history-worker.ts")],
|
|
outfile: path.join(outputDir, "decode-history-worker.js"),
|
|
bundle: true,
|
|
format: "iife",
|
|
platform: "browser",
|
|
target: "es2022",
|
|
sourcemap: false,
|
|
legalComments: "inline",
|
|
charset: "utf8",
|
|
logLevel: "info",
|
|
});
|