Release 3.8.2.6: perceived-speed UX — search, tray hide, core warm, SSE.
Server search/hotkeys; Windows close-to-tray; background EnsureCore; SSE status with rare poll fallback. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+71
-3
@@ -51,6 +51,7 @@
|
||||
const bestBtn = $("bestBtn");
|
||||
const autoBest = $("autoBest");
|
||||
const serverList = $("serverList");
|
||||
const serverSearch = $("serverSearch");
|
||||
const updCheckBtn = $("updCheckBtn");
|
||||
const updateBanner = $("updateBanner");
|
||||
const updateBtn = $("updateBtn");
|
||||
@@ -251,17 +252,35 @@
|
||||
return list;
|
||||
}
|
||||
|
||||
function filteredProfiles() {
|
||||
const q = (serverSearch && serverSearch.value || "").trim().toLowerCase();
|
||||
const list = orderedProfiles();
|
||||
if (!q) return list;
|
||||
return list.filter((p) => {
|
||||
const hay = [p.name, p.host, p.protocol].map((x) => String(x || "").toLowerCase()).join(" ");
|
||||
return hay.includes(q);
|
||||
});
|
||||
}
|
||||
|
||||
function renderServerList(activeName) {
|
||||
const active = activeName || profile.value;
|
||||
serverList.innerHTML = "";
|
||||
const list = orderedProfiles();
|
||||
if (!list.length) {
|
||||
const all = orderedProfiles();
|
||||
const list = filteredProfiles();
|
||||
if (!all.length) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "server-empty";
|
||||
empty.textContent = "Нет серверов — добавьте профиль или подписку";
|
||||
serverList.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
if (!list.length) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "server-empty";
|
||||
empty.textContent = "Ничего не найдено";
|
||||
serverList.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
list.forEach((p) => {
|
||||
const row = document.createElement("button");
|
||||
row.type = "button";
|
||||
@@ -707,11 +726,60 @@
|
||||
setMeta(String(e), "err");
|
||||
}
|
||||
})();
|
||||
if (serverSearch) {
|
||||
serverSearch.addEventListener("input", () => renderServerList(profile.value));
|
||||
}
|
||||
|
||||
function typingInField(el) {
|
||||
if (!el) return false;
|
||||
const tag = (el.tagName || "").toLowerCase();
|
||||
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && modal.classList.contains("open")) {
|
||||
modal.classList.remove("open");
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (typingInField(document.activeElement)) return;
|
||||
if ((e.key === "/" || (e.key === "f" && (e.ctrlKey || e.metaKey))) && serverSearch) {
|
||||
e.preventDefault();
|
||||
serverSearch.focus();
|
||||
serverSearch.select();
|
||||
return;
|
||||
}
|
||||
if ((e.key === "Enter" || e.key === " ") && !busy) {
|
||||
e.preventDefault();
|
||||
btn.click();
|
||||
}
|
||||
});
|
||||
|
||||
// Safety-net poll; primary updates come from SSE /api/events when available.
|
||||
setInterval(() => {
|
||||
if (document.hidden || busy || modal.classList.contains("open")) return;
|
||||
refresh().catch(() => {});
|
||||
}, 4000);
|
||||
}, 12000);
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (!document.hidden && !busy) refresh({ force: true }).catch(() => {});
|
||||
});
|
||||
|
||||
(function startEventStream() {
|
||||
if (!window.__navisHttp || typeof EventSource === "undefined") return;
|
||||
const tok = window.__NAVIS_TOKEN__ || "";
|
||||
const url = "/api/events" + (tok ? ("?t=" + encodeURIComponent(tok)) : "");
|
||||
let es;
|
||||
const connectES = () => {
|
||||
try { if (es) es.close(); } catch (_) {}
|
||||
es = new EventSource(url);
|
||||
es.onmessage = () => {
|
||||
if (!busy) refresh({ force: true }).catch(() => {});
|
||||
};
|
||||
es.onerror = () => {
|
||||
try { es.close(); } catch (_) {}
|
||||
setTimeout(connectES, 2500);
|
||||
};
|
||||
};
|
||||
connectES();
|
||||
})();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user