Files
konradandCursor 2c621d4b7f Let the config choose protocol 2 or 3, and fall back for one session only.
The default stays v2. A reflector that refuses that version and names the other gets one session of it, then the station announces the configured protocol again.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 16:28:42 +02:00

279 lines
6.9 KiB
JavaScript

const button = document.getElementById("ptt");
const config = document.getElementById("config");
const configForm = document.getElementById("config-form");
const configError = document.getElementById("config-error");
const authKey = document.getElementById("authKey");
const authToggle = document.getElementById("auth-toggle");
let savedSize = null;
let configLoading = false;
let sizeToken = 0;
function api() {
if (!window.go || !window.go.main || !window.go.main.App) {
return null;
}
return window.go.main.App;
}
function line(state) {
if (state.detail && state.detail !== "Idle") {
return state.detail;
}
if (state.talker) {
return state.talker;
}
return "Idle";
}
function runtime() {
return window.runtime || null;
}
function fitConfigWindow() {
const rt = runtime();
if (!rt || !rt.WindowGetSize || !rt.WindowSetSize) {
return;
}
const token = ++sizeToken;
requestAnimationFrame(function () {
if (token !== sizeToken || config.hidden) {
return;
}
rt.WindowGetSize().then(function (size) {
if (token !== sizeToken || config.hidden || !size) {
return;
}
if (!savedSize) {
savedSize = size;
}
const needed = Math.ceil(configForm.getBoundingClientRect().height) + 32;
const width = Math.max(size.w, 420);
const applied = rt.WindowSetSize(width, needed);
const after = applied && applied.then ? applied.then(function () {
return rt.WindowGetSize();
}) : Promise.resolve(null);
return after.then(function (now) {
if (!now || token !== sizeToken || config.hidden) {
return;
}
const extra = Math.ceil(document.documentElement.scrollHeight - window.innerHeight);
if (extra > 1) {
rt.WindowSetSize(now.w, now.h + extra);
}
});
});
});
}
function restoreWindow() {
sizeToken++;
const rt = runtime();
if (!savedSize || !rt || !rt.WindowSetSize) {
savedSize = null;
return;
}
rt.WindowSetSize(savedSize.w, savedSize.h);
savedSize = null;
}
function showError(message) {
configError.hidden = !message;
configError.textContent = message || "";
if (!config.hidden) {
fitConfigWindow();
}
}
function fillForm(form) {
document.getElementById("host").value = form.host || "";
document.getElementById("port").value = String(form.port || 0);
document.getElementById("callsign-field").value = form.callsign || "";
authKey.value = form.authKey || "";
document.getElementById("talkGroup").value = String(form.talkGroup || 0);
document.getElementById("protocol-field").value = form.protocol === "v3" ? "v3" : "v2";
document.getElementById("certificateDir").value = form.certificateDir || "";
document.getElementById("enableLogging").checked = !!form.enableLogging;
document.getElementById("logFile").value = form.logFile || "";
authKey.type = "password";
authToggle.textContent = "Show";
}
function readForm() {
return {
host: document.getElementById("host").value,
port: numberValue("port"),
callsign: document.getElementById("callsign-field").value,
authKey: authKey.value,
talkGroup: numberValue("talkGroup"),
protocol: document.getElementById("protocol-field").value,
certificateDir: document.getElementById("certificateDir").value,
enableLogging: document.getElementById("enableLogging").checked,
logFile: document.getElementById("logFile").value
};
}
function numberValue(id) {
const text = document.getElementById(id).value.trim();
if (text === "") {
return 0;
}
const n = Number(text);
if (!Number.isInteger(n)) {
return 0;
}
return n;
}
function showConfig() {
const app = api();
if (!app || configLoading) {
return;
}
configLoading = true;
app.Config().then(function (cfg) {
if (!cfg.open) {
return;
}
fillForm(cfg.form || {});
config.hidden = false;
document.body.classList.add("config-open");
showError(cfg.error);
}).catch(function () {}).then(function () {
configLoading = false;
});
}
function hideConfig() {
config.hidden = true;
document.body.classList.remove("config-open");
configError.hidden = true;
configError.textContent = "";
restoreWindow();
}
function render(state) {
document.getElementById("connection").textContent = state.connection;
document.getElementById("callsign").textContent = state.callsign;
document.getElementById("talkgroup").textContent = String(state.talkGroup);
const protocol = document.getElementById("protocol");
protocol.textContent = state.protocol ? "Protocol " + state.protocol : "";
document.getElementById("detail").textContent = line(state);
button.classList.toggle("down", !!state.transmitting);
button.disabled = !!state.configOpen;
if (state.configOpen && config.hidden) {
showConfig();
} else if (!state.configOpen && !config.hidden) {
hideConfig();
}
}
function refresh() {
const app = api();
if (!app) {
return;
}
app.State().then(render).catch(function () {});
}
function cancelConfig() {
const app = api();
if (app) {
app.CancelConfig();
}
}
document.getElementById("gear").addEventListener("click", function () {
const app = api();
if (app) {
app.OpenConfig();
}
});
configForm.addEventListener("submit", function (event) {
event.preventDefault();
const app = api();
if (!app) {
return;
}
app.Save(readForm()).then(function () {
refresh();
}).catch(function () {
app.Config().then(function (cfg) {
showError(cfg.error);
}).catch(function () {});
});
});
document.getElementById("cancel").addEventListener("click", cancelConfig);
authToggle.addEventListener("click", function () {
const show = authKey.type === "password";
authKey.type = show ? "text" : "password";
authToggle.textContent = show ? "Hide" : "Show";
});
button.addEventListener("pointerdown", function (event) {
if (button.disabled) {
return;
}
event.preventDefault();
button.setPointerCapture(event.pointerId);
const app = api();
if (app) {
app.HoldButton();
}
});
function releaseButton() {
const app = api();
if (app) {
app.ReleaseButton();
}
}
button.addEventListener("pointerup", releaseButton);
button.addEventListener("pointercancel", releaseButton);
let spaceHeld = false;
window.addEventListener("keydown", function (event) {
if (event.code === "Escape") {
if (!config.hidden) {
event.preventDefault();
cancelConfig();
}
return;
}
if (event.code !== "Space") {
return;
}
event.preventDefault();
if (!config.hidden || button.disabled || event.repeat || spaceHeld) {
return;
}
spaceHeld = true;
const app = api();
if (app) {
app.HoldSpace();
}
});
window.addEventListener("keyup", function (event) {
if (event.code !== "Space") {
return;
}
event.preventDefault();
if (!spaceHeld) {
return;
}
spaceHeld = false;
const app = api();
if (app) {
app.ReleaseSpace();
}
});
setInterval(refresh, 100);
refresh();