weee
This commit is contained in:
@@ -178,6 +178,21 @@ body {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.vs-song-art-cell {
|
||||
width: 48px;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.vs-song-art {
|
||||
display: block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 0.25rem;
|
||||
object-fit: cover;
|
||||
background: var(--bs-secondary-bg);
|
||||
}
|
||||
|
||||
/* --- Player bar --- */
|
||||
|
||||
.vs-player {
|
||||
@@ -352,3 +367,9 @@ body {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* Inline page transitions keep the persistent player mounted. */
|
||||
.vs-main[aria-busy="true"] {
|
||||
opacity: .65;
|
||||
transition: opacity 120ms ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
+129
-1
@@ -474,7 +474,134 @@
|
||||
form.appendChild(input);
|
||||
});
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
form.requestSubmit();
|
||||
});
|
||||
}
|
||||
|
||||
// --- Inline navigation ---
|
||||
// Fetch server-rendered pages and replace only the main content area. The
|
||||
// audio element and player live outside that area, so playback continues
|
||||
// uninterrupted while the URL, title, and active sidebar item still track
|
||||
// the current page normally.
|
||||
function initInlineNavigation() {
|
||||
var pending = null;
|
||||
var currentURL = location.href;
|
||||
history.replaceState({ vsInline: true, scrollY: window.scrollY }, "", currentURL);
|
||||
|
||||
function isLocalHTTP(url) {
|
||||
return (url.protocol === "http:" || url.protocol === "https:") && url.origin === location.origin;
|
||||
}
|
||||
|
||||
function shouldHandleLink(link, ev) {
|
||||
if (!link || ev.defaultPrevented || ev.button !== 0) return false;
|
||||
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return false;
|
||||
if (link.target || link.hasAttribute("download") || link.dataset.noInline != null) return false;
|
||||
var url = new URL(link.href, location.href);
|
||||
if (!isLocalHTTP(url)) return false;
|
||||
if (url.pathname.indexOf("/static/") === 0 || url.pathname.indexOf("/stream/") === 0 ||
|
||||
url.pathname.indexOf("/cover-art/") === 0 || url.pathname.indexOf("/share/") === 0 ||
|
||||
/\/export$/.test(url.pathname)) return false;
|
||||
// Let the browser handle an in-page fragment without a network request.
|
||||
if (url.pathname === location.pathname && url.search === location.search && url.hash) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function setBusy(busy) {
|
||||
var main = document.querySelector(".vs-main");
|
||||
if (!main) return;
|
||||
if (busy) main.setAttribute("aria-busy", "true");
|
||||
else main.removeAttribute("aria-busy");
|
||||
}
|
||||
|
||||
function applyPage(html, responseURL, push, restoreScroll) {
|
||||
var parsed = new DOMParser().parseFromString(html, "text/html");
|
||||
var nextMain = parsed.querySelector(".vs-main");
|
||||
var oldMain = document.querySelector(".vs-main");
|
||||
// Login, setup, shared, and error documents are intentionally full-page.
|
||||
if (!nextMain || !oldMain) return false;
|
||||
|
||||
oldMain.replaceWith(nextMain);
|
||||
document.title = parsed.title || document.title;
|
||||
var tokenScript = parsed.querySelector("script[data-vs-csrf]");
|
||||
if (tokenScript) window.vsCSRFToken = tokenScript.dataset.vsCsrf;
|
||||
|
||||
var activeHref = parsed.querySelector(".vs-sidebar .nav-link.active");
|
||||
document.querySelectorAll(".vs-sidebar .nav-link").forEach(function (link) {
|
||||
link.classList.toggle("active", !!activeHref && link.getAttribute("href") === activeHref.getAttribute("href"));
|
||||
});
|
||||
|
||||
currentURL = responseURL;
|
||||
if (push) history.pushState({ vsInline: true, scrollY: 0 }, "", responseURL);
|
||||
var top = typeof restoreScroll === "number" ? restoreScroll : 0;
|
||||
window.scrollTo(0, top);
|
||||
var autofocus = nextMain.querySelector("[autofocus]");
|
||||
if (autofocus) autofocus.focus({ preventScroll: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
function navigate(url, options) {
|
||||
options = options || {};
|
||||
if (pending) pending.abort();
|
||||
var controller = new AbortController();
|
||||
pending = controller;
|
||||
setBusy(true);
|
||||
var request = {
|
||||
method: options.method || "GET",
|
||||
body: options.body || null,
|
||||
signal: controller.signal,
|
||||
headers: { "X-VibeSonic-Inline": "1" },
|
||||
};
|
||||
return fetch(url, request).then(function (response) {
|
||||
if (!response.ok || !(response.headers.get("Content-Type") || "").includes("text/html")) {
|
||||
throw new Error("not an inline page");
|
||||
}
|
||||
return response.text().then(function (html) {
|
||||
if (!applyPage(html, response.url, options.push !== false, options.restoreScroll)) {
|
||||
location.assign(response.url);
|
||||
}
|
||||
});
|
||||
}).catch(function (err) {
|
||||
if (err.name !== "AbortError") {
|
||||
if ((options.method || "GET") === "GET") location.assign(url);
|
||||
else location.reload();
|
||||
}
|
||||
}).finally(function () {
|
||||
if (pending === controller) {
|
||||
pending = null;
|
||||
setBusy(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("click", function (ev) {
|
||||
var link = ev.target.closest("a[href]");
|
||||
if (!shouldHandleLink(link, ev)) return;
|
||||
ev.preventDefault();
|
||||
history.replaceState({ vsInline: true, scrollY: window.scrollY }, "", currentURL);
|
||||
navigate(link.href);
|
||||
});
|
||||
|
||||
document.addEventListener("submit", function (ev) {
|
||||
var form = ev.target;
|
||||
if (ev.defaultPrevented || !(form instanceof HTMLFormElement) || form.dataset.noInline != null) return;
|
||||
var url = new URL(form.action || location.href, location.href);
|
||||
if (!isLocalHTTP(url) || url.pathname === "/logout") return;
|
||||
ev.preventDefault();
|
||||
var data = new FormData(form);
|
||||
var method = (form.method || "GET").toUpperCase();
|
||||
if (method === "GET") {
|
||||
url.search = new URLSearchParams(data).toString();
|
||||
navigate(url.href);
|
||||
} else {
|
||||
navigate(url.href, { method: method, body: data });
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("popstate", function (ev) {
|
||||
navigate(location.href, {
|
||||
push: false,
|
||||
restoreScroll: ev.state && typeof ev.state.scrollY === "number" ? ev.state.scrollY : 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -483,5 +610,6 @@
|
||||
var player = initPlayer();
|
||||
initKeyboardShortcuts(player);
|
||||
initStarButtons();
|
||||
initInlineNavigation();
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user