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:
@@ -801,3 +801,18 @@
|
||||
.tools { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
|
||||
.log-tail {
|
||||
max-height: 160px;
|
||||
overflow: auto;
|
||||
font-size: .72rem;
|
||||
line-height: 1.35;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
background: var(--panel-bg, rgba(0,0,0,.04));
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.row-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
|
||||
|
||||
+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 });
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="brand-wrap">
|
||||
<div class="brand-row">
|
||||
<h1 class="brand">Navis</h1>
|
||||
<span class="badge-ver" id="badgeVer">3.8.1</span>
|
||||
<span class="badge-ver" id="badgeVer">3.8.2</span>
|
||||
</div>
|
||||
<p class="tagline">Быстрый клиент · Naive · Hy2 · AWG · Xray</p>
|
||||
</div>
|
||||
@@ -122,6 +122,29 @@
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>Подключать при запуске</span>
|
||||
<label class="switch">
|
||||
<input id="bootConnect" type="checkbox" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>Автопереподключение</span>
|
||||
<label class="switch">
|
||||
<input id="autoReconnect" type="checkbox" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<details class="panel" id="logBox">
|
||||
<summary>Логи ядра</summary>
|
||||
<pre class="log-tail" id="logTail">—</pre>
|
||||
<div class="row-actions">
|
||||
<button class="action secondary" id="logRefreshBtn" type="button">Обновить лог</button>
|
||||
<button class="action secondary" id="probeBtn" type="button">Проверить туннель</button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="panel hy2" id="hy2Box">
|
||||
<summary>Hysteria 2 · BBR / obfuscation</summary>
|
||||
|
||||
Reference in New Issue
Block a user