v0.26.3
This commit is contained in:
1 parent
32e6875980
commit
cf25423dde
68 files changed
+2314
-422
No files matched your search
@@ -2,6 +2,7 @@
|
||||
// Persistent tab DOM: switching tabs never discards unsaved form values.
|
||||
(() => {
|
||||
const buttons = [...document.querySelectorAll("[data-settings-tab]")];
|
||||
const search = $("settings-search"), searchStatus = $("settings-search-status");
|
||||
const defaultTab = (typeof location !== "undefined" && location.pathname === "/configuration") ? "manual-config" : "listeners";
|
||||
const requestedTab = typeof location !== "undefined" && location.pathname === "/logging"
|
||||
? "logging"
|
||||
@@ -9,6 +10,17 @@
|
||||
const tabAliases = { http2: "connections" };
|
||||
const normalizedRequestedTab = tabAliases[requestedTab] || requestedTab;
|
||||
let active = buttons.some(button => button.dataset.settingsTab === normalizedRequestedTab) ? normalizedRequestedTab : defaultTab;
|
||||
function applySettingsSearch() {
|
||||
const query = String(search?.value || "").trim().toLocaleLowerCase();
|
||||
const matches = [];
|
||||
for (const button of buttons) {
|
||||
const page = document.querySelector(`[data-settings-page="${CSS.escape(button.dataset.settingsTab)}"]`);
|
||||
const haystack = `${button.textContent || ""} ${page?.textContent || ""}`.toLocaleLowerCase();
|
||||
const match = !query || haystack.includes(query); button.hidden = !match; if (match) matches.push(button);
|
||||
}
|
||||
if (searchStatus) searchStatus.textContent = query ? `${t("settingsSearchResults")}: ${matches.length}` : "";
|
||||
if (query && !matches.some(button => button.dataset.settingsTab === active) && matches[0]) select(matches[0].dataset.settingsTab, false, false);
|
||||
}
|
||||
function syncLocation() {
|
||||
if (typeof location === "undefined" || !["/settings", "/configuration", "/logging"].includes(location.pathname) || !["http:", "https:"].includes(location.protocol)) return;
|
||||
const url = new URL(location.href); const alreadyCanonical = url.pathname === "/settings" && url.searchParams.get("tab") === active; if (alreadyCanonical) return;
|
||||
@@ -16,18 +28,21 @@
|
||||
history.replaceState({ ...(history.state || {}), panel: "settings", settingsTab: active }, "", `${url.pathname}${url.search}${url.hash}`);
|
||||
}
|
||||
function select(name, focus = false, persist = true) {
|
||||
if (!buttons.some(b => b.dataset.settingsTab === name)) return; active = name;
|
||||
const target = buttons.find(button => button.dataset.settingsTab === name); if (!target) return;
|
||||
if (target.hidden && search?.value) { search.value = ""; applySettingsSearch(); }
|
||||
active = name;
|
||||
for (const b of buttons) { const chosen = b.dataset.settingsTab === name; b.setAttribute("aria-selected", String(chosen)); b.tabIndex = chosen ? 0 : -1; if (chosen && focus) b.focus(); }
|
||||
document.querySelectorAll("[data-settings-page]").forEach(p => p.hidden = p.dataset.settingsPage !== name);
|
||||
if (persist) syncLocation();
|
||||
}
|
||||
for (const b of buttons) {
|
||||
b.addEventListener("click", () => select(b.dataset.settingsTab)); b.addEventListener("keydown", event => {
|
||||
const index = buttons.indexOf(b); let next;
|
||||
if (event.key === "ArrowRight" || event.key === "ArrowDown") next = (index + 1) % buttons.length; else if (event.key === "ArrowLeft" || event.key === "ArrowUp") next = (index + buttons.length - 1) % buttons.length; else if (event.key === "Home") next = 0; else if (event.key === "End") next = buttons.length - 1; else return;
|
||||
event.preventDefault(); select(buttons[next].dataset.settingsTab, true);
|
||||
const visible = buttons.filter(button => !button.hidden), index = visible.indexOf(b); let next;
|
||||
if (event.key === "ArrowRight" || event.key === "ArrowDown") next = (index + 1) % visible.length; else if (event.key === "ArrowLeft" || event.key === "ArrowUp") next = (index + visible.length - 1) % visible.length; else if (event.key === "Home") next = 0; else if (event.key === "End") next = visible.length - 1; else return;
|
||||
if (!visible.length) return; event.preventDefault(); select(visible[next].dataset.settingsTab, true);
|
||||
});
|
||||
}
|
||||
search?.addEventListener("input", applySettingsSearch);
|
||||
const cacheTabs = [...document.querySelectorAll("[data-cache-settings-tab]")];
|
||||
function selectCacheTab(name, focus = false) {
|
||||
if (!cacheTabs.some(button => button.dataset.cacheSettingsTab === name)) return;
|
||||
@@ -101,7 +116,7 @@ curl --fail --show-error --request POST \
|
||||
const info = el("div", undefined, "certificate-api-credential-info");
|
||||
info.append(el("b", credential.key_id || "-", "mono"));
|
||||
if (credential.comment) info.append(el("span", credential.comment, "fine"));
|
||||
const lastUsed = credential.last_used_at ? new Date(Number(credential.last_used_at) * 1000).toLocaleString() : t("certificateApiNeverUsed");
|
||||
const lastUsed = credential.last_used_at ? formatDateTime(Number(credential.last_used_at) * 1000) : t("certificateApiNeverUsed");
|
||||
info.append(el("span", `${t("certificateApiLastUsed")}: ${lastUsed}`, "fine"));
|
||||
const remove = action(t("certificateApiRevoke"), async () => {
|
||||
if (!await appConfirm(t("certificateApiRevokeOneConfirm").replace("{key}", credential.key_id), { danger: true })) return;
|
||||
@@ -206,6 +221,6 @@ curl --fail --show-error --request POST \
|
||||
const text = await response.text(); if (epoch !== authEpoch || !authenticated) return; $("prom-preview-output").textContent = text.slice(0, 262144); $("prom-preview-output").hidden = false;
|
||||
} catch (error) { if (epoch === authEpoch) toast(error.message, true); } finally { button.disabled = false; }
|
||||
});
|
||||
window.rustProxySettings = { select, selectHttpTab, syncLocation, render, current: () => active, reset() { select("listeners", false, false); selectCacheTab("general"); selectLoggingTab("outputs"); selectHttpTab("http2"); certificateApiState = null; certificateApiLoading = false; $("certificate-api-key-id").value = ""; $("certificate-api-comment").value = ""; $("certificate-api-token").value = ""; $("certificate-api-secret").textContent = ""; $("certificate-api-credentials").replaceChildren(); $("certificate-api-secret-wrap").hidden = true; $("prom-preview-output").textContent = ""; $("prom-preview-output").hidden = true; } };
|
||||
window.rustProxySettings = { select, selectHttpTab, syncLocation, render, refreshSearch: applySettingsSearch, current: () => active, reset() { if (search) search.value = ""; applySettingsSearch(); select("listeners", false, false); selectCacheTab("general"); selectLoggingTab("outputs"); selectHttpTab("http2"); certificateApiState = null; certificateApiLoading = false; $("certificate-api-key-id").value = ""; $("certificate-api-comment").value = ""; $("certificate-api-token").value = ""; $("certificate-api-secret").textContent = ""; $("certificate-api-credentials").replaceChildren(); $("certificate-api-secret-wrap").hidden = true; $("prom-preview-output").textContent = ""; $("prom-preview-output").hidden = true; } };
|
||||
select(active); render();
|
||||
})();
|
||||
Reference in new issue
Block a user