Release 3.8.2.1: reliability, UI probe/logs, CI, signing gates.
Clear sysproxy on core death; probe+reconnect; subscription prune; Best ignores soft UDP; Windows tray; Android protocol gate; CI workflows. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+40
-4
@@ -3,7 +3,7 @@
|
||||
const methods = [
|
||||
"getState","getEditState","connect","disconnect","connectProfile","saveProfile","createProfile",
|
||||
"selectProfile","deleteProfile","installCore","openURL","pingServers","pingBest",
|
||||
"checkUpdate","applyUpdate","saveHy2","importSubscription","quit"
|
||||
"checkUpdate","applyUpdate","saveHy2","importSubscription","getLogs","probeTunnel","savePrefs","quit"
|
||||
];
|
||||
if (typeof window.getState === "function") return;
|
||||
window.__navisHttp = true;
|
||||
@@ -36,6 +36,11 @@
|
||||
const proxy = $("proxy");
|
||||
const nameInput = $("name");
|
||||
const sysproxy = $("sysproxy");
|
||||
const bootConnect = $("bootConnect");
|
||||
const autoReconnect = $("autoReconnect");
|
||||
const logTail = $("logTail");
|
||||
const logRefreshBtn = $("logRefreshBtn");
|
||||
const probeBtn = $("probeBtn");
|
||||
const profile = $("profile");
|
||||
const dot = $("dot");
|
||||
const statusText = $("statusText");
|
||||
@@ -213,7 +218,7 @@
|
||||
parts.push(p.name + "|" + (p.protocol || "") + "|" + (p.host || ""));
|
||||
});
|
||||
(state.pings || []).forEach((p) => {
|
||||
parts.push(p.name + "|" + (p.ok ? 1 : 0) + "|" + (p.ms || 0) + "|" + (p.error || ""));
|
||||
parts.push(p.name + "|" + (p.ok ? 1 : 0) + "|" + (p.soft ? 1 : 0) + "|" + (p.ms || 0) + "|" + (p.error || ""));
|
||||
});
|
||||
return parts.join("\n");
|
||||
}
|
||||
@@ -282,7 +287,8 @@
|
||||
const pr = pingMap[p.name];
|
||||
if (pr && pr.ok) {
|
||||
ms.className = "ms " + msClass(pr.ms, true);
|
||||
ms.textContent = pr.ms + " ms";
|
||||
ms.textContent = (pr.soft ? "~" : "") + pr.ms + " ms";
|
||||
if (pr.soft) ms.title = "soft-up (UDP без ответа) — не выбирается как «Лучший»";
|
||||
} else if (pr && pr.error) {
|
||||
ms.className = "ms bad";
|
||||
ms.textContent = "—";
|
||||
@@ -363,6 +369,10 @@
|
||||
proxy.disabled = lock;
|
||||
nameInput.disabled = lock;
|
||||
sysproxy.disabled = lock;
|
||||
if (bootConnect) bootConnect.disabled = busy;
|
||||
if (autoReconnect) autoReconnect.disabled = busy;
|
||||
if (probeBtn) probeBtn.disabled = busy || !connected;
|
||||
if (logRefreshBtn) logRefreshBtn.disabled = busy;
|
||||
profile.disabled = lock;
|
||||
addBtn.disabled = lock;
|
||||
delBtn.disabled = lock || ((state.profiles || profiles).length <= 1);
|
||||
@@ -392,6 +402,8 @@
|
||||
nameInput.value = active.name || state.active_profile || "";
|
||||
proxy.value = typeof state.proxy === "string" ? state.proxy : (active.proxy || "");
|
||||
if (typeof state.system_proxy === "boolean") sysproxy.checked = state.system_proxy;
|
||||
if (bootConnect && typeof state.connect_on_launch === "boolean") bootConnect.checked = state.connect_on_launch;
|
||||
if (autoReconnect && typeof state.auto_reconnect === "boolean") autoReconnect.checked = state.auto_reconnect;
|
||||
fillHy2(state.hy2);
|
||||
formHydrated = true;
|
||||
if (syncForm) dirty = false;
|
||||
@@ -420,11 +432,17 @@
|
||||
heroHint.textContent = n > 1 ? "Клик — выбрать, двойной клик — подключить" : "Выберите сервер и нажмите Подключить";
|
||||
}
|
||||
if (!busy && Date.now() > metaHoldUntil) {
|
||||
setMeta(detail, state.core_ready === false ? "err" : "");
|
||||
if (!connected && state.last_error) setMeta(state.last_error, "err");
|
||||
else setMeta(detail, state.core_ready === false ? "err" : "");
|
||||
metaHoldUntil = 0;
|
||||
}
|
||||
}
|
||||
|
||||
async function persistPrefs() {
|
||||
if (typeof savePrefs !== "function") return;
|
||||
await savePrefs(!!(bootConnect && bootConnect.checked), !!(autoReconnect && autoReconnect.checked), !!sysproxy.checked);
|
||||
}
|
||||
|
||||
async function refresh(opts) {
|
||||
const needSecrets = !!(opts && opts.syncForm) || (!formHydrated && !dirty);
|
||||
let state;
|
||||
@@ -653,6 +671,24 @@
|
||||
setMeta("Версия " + (latest || "?") + " пропущена. Следующую предложим.", "ok");
|
||||
});
|
||||
|
||||
if (sysproxy) sysproxy.addEventListener("change", () => { persistPrefs().catch(() => {}); dirty = true; });
|
||||
if (bootConnect) bootConnect.addEventListener("change", () => { persistPrefs().catch(() => {}); });
|
||||
if (autoReconnect) autoReconnect.addEventListener("change", () => { persistPrefs().catch(() => {}); });
|
||||
if (logRefreshBtn) logRefreshBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
const t = await getLogs();
|
||||
if (logTail) logTail.textContent = t || "—";
|
||||
setMeta("Лог обновлён", "ok");
|
||||
} catch (e) { setMeta(fmtErr(e), "err"); }
|
||||
}));
|
||||
if (probeBtn) probeBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
setMeta("Проверка туннеля…");
|
||||
await probeTunnel();
|
||||
setMeta("Туннель OK", "ok");
|
||||
} catch (e) { setMeta(fmtErr(e), "err"); }
|
||||
}));
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await refresh({ syncForm: true });
|
||||
|
||||
Reference in New Issue
Block a user