Release 3.8.2.12: export/import full server list as JSON.
ci / test (macos-latest) (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run

Save and replace profiles via system file dialog; buttons after Best; prefs unchanged on import.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
M4
2026-08-01 17:51:38 +03:00
co-authored by Cursor
parent 57d719f02f
commit 8b564110d5
22 changed files with 604 additions and 22 deletions
+38 -2
View File
@@ -3,7 +3,8 @@
const methods = [
"getState","getEditState","connect","disconnect","connectProfile","saveProfile","createProfile",
"selectProfile","deleteProfile","installCore","openURL","pingServers","pingBest",
"checkUpdate","applyUpdate","saveHy2","importSubscription","getLogs","probeTunnel","savePrefs","quit"
"checkUpdate","applyUpdate","saveHy2","importSubscription","exportServers","importServers",
"getLogs","probeTunnel","savePrefs","quit"
];
if (typeof window.getState === "function") return;
window.__navisHttp = true;
@@ -49,6 +50,8 @@
const shopLink = $("shopLink");
const pingBtn = $("pingBtn");
const bestBtn = $("bestBtn");
const exportServersBtn = $("exportServersBtn");
const importServersBtn = $("importServersBtn");
const autoBest = $("autoBest");
const serverList = $("serverList");
const serverSearch = $("serverSearch");
@@ -425,6 +428,8 @@
coreBtn.disabled = busy;
pingBtn.disabled = busy;
bestBtn.disabled = busy;
if (exportServersBtn) exportServersBtn.disabled = busy;
if (importServersBtn) importServersBtn.disabled = busy || connected;
updCheckBtn.disabled = busy;
updateBtn.disabled = busy;
subBtn.disabled = busy;
@@ -536,7 +541,7 @@
}
function paintButtonsLocked(locked) {
[btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, updCheckBtn, updateBtn, skipUpdateBtn, subBtn, subUrl].forEach((b) => {
[btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, exportServersBtn, importServersBtn, updCheckBtn, updateBtn, skipUpdateBtn, subBtn, subUrl].forEach((b) => {
if (b) b.disabled = locked;
});
}
@@ -699,6 +704,37 @@
} catch (e) { setMeta(String(e), "err"); }
}));
if (exportServersBtn) exportServersBtn.addEventListener("click", () => withBusy(async () => {
try {
setMeta("Экспорт списка…");
const n = await exportServers();
if (!n) {
setMeta("Экспорт отменён");
return;
}
setMeta("Экспортировано серверов: " + n, "ok");
} catch (e) { setMeta(fmtErr(e), "err"); }
}));
if (importServersBtn) importServersBtn.addEventListener("click", () => withBusy(async () => {
try {
if (!confirm("Заменить весь список серверами из файла? Текущие профили будут удалены.")) {
return;
}
setMeta("Импорт списка…");
const n = await importServers();
if (!n) {
setMeta("Импорт отменён");
return;
}
pingMap = {};
dirty = false;
formHydrated = false;
await refresh({ syncForm: true, force: true });
setMeta("Импортировано серверов: " + n + " (список заменён)", "ok");
} catch (e) { setMeta(fmtErr(e), "err"); }
}));
updCheckBtn.addEventListener("click", () => withBusy(async () => {
try {
setMeta("Проверка обновлений…");